bostrat-node 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +30 -30
  2. package/dist/index.js +98 -98
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,88 +1,88 @@
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*$/,"")}
1
+ var Gh=Object.defineProperty;var Yc=(t,e)=>{for(var n in e)Gh(t,n,{get:e[n],enumerable:!0})};import ct from"node:path";import It from"node:fs";import aA from"node:readline";import{fileURLToPath as iA}from"node:url";import de from"express";import cA from"node:http";import lA from"node:https";import zc from"node:os";import{WebSocketServer as uA}from"ws";import{execFile as ew}from"node:child_process";import{promisify as nw}from"node:util";import Ps from"node:fs";import{randomUUID as rw}from"node:crypto";import gn from"node:fs/promises";import Zc from"node:os";import Le from"node:path";function Qr(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Kh(){return process.env.CLAUDE_CONFIG_DIR?Le.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Le.join(Zc.homedir(),".claude.json")}function Vh(){return process.env.CLAUDE_CONFIG_DIR?Le.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Le.join(Zc.homedir(),".claude","settings.json")}async function fe(t){if(!t||typeof t!="string")return;let e=Le.resolve(t),n=Kh(),r=Vh(),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 Cs from"node:fs/promises";import Jh from"node:os";import to from"node:path";function zh(){let t=process.env.CODEX_HOME||to.join(Jh.homedir(),".codex");return to.join(t,"config.toml")}function Qc(t){return`[projects."${t}"]`}function Xh(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 yn(t){if(!t||typeof t!="string")return;let e=to.resolve(t),n=zh(),r="";try{r=await Cs.readFile(n,"utf8")}catch{}if(Xh(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*$/,"")}
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 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)}
8
+ `:"")}try{await Cs.mkdir(to.dirname(n),{recursive:!0}),await Cs.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import tl from"node:fs/promises";import Yh from"node:os";import or from"node:path";function Zh(t){let n=or.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||or.join(Yh.homedir(),".cursor");return or.join(r,"projects",n)}async function bn(t){if(!t||typeof t!="string")return;let e=or.resolve(t),n=Zh(e),r=or.join(n,".workspace-trusted");await tl.mkdir(n,{recursive:!0}),await tl.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
9
+ `,"utf8")}import sr from"node:fs/promises";import Qh from"node:os";import nl from"node:path";var Ms=process.env.BOSTRAT_DEBUG_DIR||nl.join(Qh.homedir(),".bostrat","debug"),kn=nl.join(Ms,"handoff.jsonl"),el=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function eo(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function tw(t){try{await sr.mkdir(Ms,{recursive:!0}),await sr.appendFile(kn,`${JSON.stringify(t)}
10
10
  `,"utf8");let n=(await sr.readFile(kn,"utf8")).split(`
11
- `).filter(Boolean);n.length>Qc&&await sr.writeFile(kn,`${n.slice(-Qc).join(`
11
+ `).filter(Boolean);n.length>el&&await sr.writeFile(kn,`${n.slice(-el).join(`
12
12
  `)}
13
- `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${kn}: ${e?.message||e}`)}}async function L(t={}){let e={at:new Date().toISOString(),ts:Date.now(),...t},n=[e.flow||"handoff",e.phase||e.step||"?",e.trackId?`track=${e.trackId}`:"",e.sessionName?`box=${e.sessionName}`:"",e.error?`ERR=${e.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),e.sessionName&&to())try{e.paneTail=(await pt(e.sessionName,{lines:35})).slice(-4e3)}catch{e.paneTail=null}(to()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed)/.test(String(e.phase||e.step||"")))&&await Qh(e)}async function el({limit:t=80}={}){try{return(await sr.readFile(kn,"utf8")).split(`
14
- `).filter(Boolean).slice(-t).map(r=>{try{return JSON.parse(r)}catch{return{raw:r}}})}catch{return[]}}var nl={DEBUG_DIR:Cs,LOG_FILE:kn};var nt=ew(tw);function ar(t){return new Promise(e=>setTimeout(e,t))}function Ms(t,e){if(!e)return t;let n=o=>typeof o=="string"?o.split(e).join("<redacted>"):o,r=new Error(n(t?.message)||"command failed");return t?.code!==void 0&&(r.code=t.code),t?.stderr!==void 0&&(r.stderr=n(t.stderr)),r}var O=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux",Ht=process.env.BOSTRAT_TMUX_SOCKET||"/tmp/tmux-1000/default",rw=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Ps=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ns="bostrat-usage-probe",ow=new Set([Ns]);function Bs(t,e){let n=xn(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function xn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function Sn(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}async function Ds(t,{loginShell:e=!1,failOpen:n=!1}={}){if(!t)return!1;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await nt(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function js(t){return Ds(String(t||"").trim().split(/\s+/)[0])}function C(...t){return["-S",Ht,...t]}async function ee(){if(!rl.existsSync(Ht))return!1;try{return await nt(O,C("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}var sw=/^[A-Za-z0-9_.-]{1,64}$/;function ut(t){return typeof t=="string"&&sw.test(t)}async function vn(){if(!rl.existsSync(Ht))return[];try{let t="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}",{stdout:e}=await nt(O,C("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
15
- `).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,f]=r.split("|"),w=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null)}}).filter(r=>!ow.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function G(t){return(await vn()).find(n=>n.name===t)||null}var aw=/^[A-Za-z0-9 ._-]+$/,iw=64;function cw(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=iw&&aw.test(e)}function Ls(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"")}async function lw(t){try{let{stdout:e}=await nt(O,C("list-sessions","-F","#{session_name}"));return e.split(`
16
- `).map(n=>n.trim()).includes(t)}catch{return!1}}async function Ue(t,e=rw,n={}){if(!cw(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Ls(r);if(!ut(o))throw new Error("resulting tmux session name is invalid");if(!await ee())throw new Error(`host tmux server not running on ${Ht} \u2014 start it with: systemctl --user start globo-tmux.service`);if(await lw(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Ps,a=n.agent||Sn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||nw(),l=i?s:Bs(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&xn(s),f=n.firstRun===!0;f||(d&&await pe(e),a==="codex"&&await yn(e),a==="cursor"&&await bn(e));let w=d&&!f?Zr():null,m=w?["-e",`ANTHROPIC_API_KEY=${w}`]:[];try{await nt(O,C("new-session","-d","-s",o,"-c",e,...m))}catch(g){throw w?Ms(g,w):g}return await Fe(o),await nt(O,C("set-option","-t",o,"@bostrat-session","1")),await nt(O,C("set-option","-t",o,"@bostrat-title",r)),await nt(O,C("set-option","-t",o,"@bostrat-sid",c)),await nt(O,C("set-option","-t",o,"@bostrat-agent",a)),await nt(O,C("send-keys","-t",o,l,"Enter")),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u}}async function Wt(t){if(!ut(t))throw new Error("invalid session name");await nt(O,C("kill-session","-t",t))}async function ol(t,e){if(ut(t))try{let n=e?C("set-option","-t",t,"@bostrat-sid",e):C("set-option","-u","-t",t,"@bostrat-sid");await nt(O,n)}catch{}}async function pt(t,{lines:e=40}={}){if(!ut(t))return"";try{let n=C("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await nt(O,n);return String(r||"")}catch{return""}}var uw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,dw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Rt=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,fw=Number(process.env.BOSTRAT_BOX_COLS)||120,pw=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function mw(t){if(!ut(t))return 0;try{let{stdout:e}=await nt(O,C("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
17
- `).filter(Boolean).length}catch{return 0}}async function Fe(t,{cols:e=fw,rows:n=pw}={}){if(ut(t)&&!await mw(t))try{let r=await al(t);if(r.cols===e&&r.rows===n)return;await nt(O,C("resize-window","-t",t,"-x",String(e),"-y",String(n))),await nt(O,C("set-window-option","-t",t,"window-size","latest")),await ar(80)}catch{}}async function sl(t){if(!ut(t))return 0;try{let{stdout:e}=await nt(O,C("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function al(t){if(!ut(t))return{cols:0,rows:0};try{let{stdout:e}=await nt(O,C("display-message","-t",t,"-p","#{window_width} #{window_height}")),[n,r]=String(e||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function eo(t,{cols:e=uw,rows:n=dw}={}){if(ut(t))try{let r=await al(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await nt(O,C("resize-window","-t",t,"-x",String(o),"-y",String(s))),await ar(80))}catch{}}async function me(t){return await eo(t),pt(t,{lines:Rt})}var hw=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function gt(t,e,{settleMs:n=40,kind:r}={}){if(!ut(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>hw.has(s));for(let s=0;s<o.length;s++)await nt(O,C("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await ar(n);return L({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function no(t,e,{kind:n}={}){if(!ut(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await nt(O,C("send-keys","-t",t,"-N",String(r),"BSpace")),L({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function ro(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!ut(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await nt(O,C("send-keys","-t",t,"-l",s)),n>0&&await ar(n);L({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function yt(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!ut(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await nt(O,C("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await ar(s),await nt(O,C("send-keys","-t",t,"Enter"))}L({flow:"tmux",phase:"send",sessionName:t,kind:o||"text",textLen:typeof e=="string"?e.length:0,textHead:typeof e=="string"?e.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}import ww from"node-pty";async function cl(t,e,n={}){if(!ut(e)){oo(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await ee()){oo(t,`host tmux server not running on ${Ht}`),t.close(1011,"tmux server unavailable");return}if(!await G(e)){oo(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=il(n.cols,120,ll),o=il(n.rows,32,ul),s;try{s=ww.spawn(O,["-S",Ht,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){oo(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Us(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Us(t,{type:"exit",code:a??null,signal:i??null}),t.close(1e3,"tmux exited"))}),t.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&gw(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),t.on("close",()=>{try{s.kill()}catch{}Fe(e).catch(()=>{})})}var ll=20,ul=6;function gw(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=ll&&Number(e)>=ul}function il(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Us(t,e){try{t.send(JSON.stringify(e))}catch{}}function oo(t,e){Us(t,{type:"error",message:e})}function dl(t){let e=typeof t=="string"&&t?t:"/",n=e.indexOf("?"),r=n===-1?e:e.slice(0,n),o=n===-1?"":e.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}import ir from"node:fs";import yw from"node:fs/promises";import Tn from"node:path";import bw from"node:os";var fl={"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},kw=2e5,Fs=process.env.BOSTRAT_CLAUDE_PROJECTS||Tn.join(bw.homedir(),".claude","projects");function Hs(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Ws(t){let e=Tn.join(Fs,Hs(t)),n;try{n=ir.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Tn.join(e,o);try{let{mtimeMs:a}=ir.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function ne(t,e){if(!e)return null;let n=Tn.join(Fs,Hs(t),`${e}.jsonl`);return ir.existsSync(n)?n:null}function pl(t){if(!t)return[];let e=Tn.join(Fs,Hs(t)),n;try{n=ir.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=ir.statSync(Tn.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function vt(t,e=131072){let n=await yw.open(t,"r");try{let{size:r}=await n.stat(),o=Math.max(0,r-e),s=r-o;if(s===0)return"";let a=Buffer.alloc(s);return await n.read(a,0,s,o),a.toString("utf8")}finally{await n.close()}}function xw(t){let e=t.split(`
18
- `);for(let n=e.length-1;n>=0;n--){let r=e[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function cr(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=ne(t,e),o=!0,!r)return null}else if(n){if(r=Ws(t),o=!1,!r)return null}else return null;let s;try{s=xw(await vt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(fl,s.model),i=fl[s.model]||kw;return{source:"transcript",exact:o,model:s.model,windowKnown:a,window:i,occupancy:s.occupancy,pct:Math.min(1,s.occupancy/i),transcript:r}}function Sw(t,e){return!t||!e?null:ne(t,e)}function vw(t){return typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
19
- `).trim():""}function Tw(t){if(!Array.isArray(t))return null;let e=t.filter(n=>n&&n.type==="tool_use").map(n=>n.name).filter(Boolean);return e.length?e:null}function Aw(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function qs(t){let e=[],n=t?.message?.content;if(!Array.isArray(n))return e;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)e.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&e.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return e}function ml(t,e){if(!t.uuid)return null;let n=qs(t).map((r,o)=>({uuid:t.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&e.get(r.toolUseId)||null}));return n.length?n:null}var _w=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Gs(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
20
- `)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant")continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let g=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof g=="string"&&_w.test(g)&&s.set(m.id,g.split("/").pop())}if(Aw(l.content)){let m=ml(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=vw(l.content),f=u==="assistant"?Tw(l.content):null,w=u==="user"?ml(c,s):null;!d&&!f&&!w||o.push({role:u,text:d,tools:f||void 0,images:w||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var Ew=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function hl(t,e,{turns:n=16}={}){let r=Sw(t,e);if(!r)return"";let o=await Gs(r,{limit:n});return o.length?o.map(s=>{let a=s.role==="user"?"User":"Lead agent",i=s.tools?`_(also ran: ${s.tools.join(", ")})_`:"",c=s.images?.length?"(viewed an image)":"(took an action)";return`**${a}:** ${Ew(s.text||c,700)} ${i}`.trim()}).join(`
21
-
22
- `):""}import Gw from"node:fs";import ta from"node:fs/promises";import lo from"node:path";import Iw from"node:fs/promises";import{execFile as Rw}from"node:child_process";import{promisify as $w}from"node:util";var Ks=$w(Rw),Ow=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,wl=new Set,gl=Promise.resolve(),Vs=null;function bl(t){Vs=typeof t=="function"?t:null}function Cw(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function kl({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||wl.has(r)||(wl.add(r),gl=gl.then(()=>Mw({sessionName:t,from:e,to:r})).catch(()=>{}))}async function Mw({sessionName:t,from:e,to:n}){await L({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Ks("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await Iw.realpath(r),s=Cw(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Ks("npm",["install","-g","@openai/codex"],{env:a,timeout:Ow});let i=(await Ks(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await L({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await yl({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await L({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await yl({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function yl(t){if(Vs)try{await Vs(t)}catch{}}var Pw=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Js=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,lr=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,$t={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key/},cursor:{up:/Run Everything|Add a follow-up|Plan, search, build anything/,busy:/ctrl\+c to stop|^\s*[⠁-⣿]+\s+\S/m,block:/Workspace Trust Required|Trust this workspace|\[a\]\s*Trust|Trusting workspace/}},Nw=/Trust this workspace|\[a\]\s*Trust/,Bw=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function Dw(t){let e=String(t||"");if(!$t.codex.block.test(e))return null;let n=e.match(Bw);return{from:n?.[1]||null,to:n?.[2]||null}}function he(t){return new Promise(e=>setTimeout(e,t))}var jw=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,so=new Map;function xl(t,e){let n=Date.now(),r=jw();for(let[s,a]of so)n-a>=r&&so.delete(s);let o=`${t}::${e}`;return so.has(o)?!1:(so.set(o,n),!0)}var Lw=68,Uw=/^\s*[─━]{10,}\s*$/;function Fw(t){let e=0;for(let n of String(t).split(`
23
- `))Uw.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function zs(t,e){let n=$t[t];if(!n?.busy)return"idle";if(Xs(t,e))return"unknown";if(n.up&&!n.up.test(e))return"idle";if(n.busy.test(e))return"busy";let r=Fw(e);return t==="claude"&&r>0&&r<Lw?"unknown":"idle"}var Hw=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/;function vl(t,e){return t==="claude"&&Hw.test(String(e||""))}function ur(t,e){return zs(t,e)==="busy"}function ao(t,e){return zs(t,e)!=="idle"}function io(t,e){let n=$t[t];return n?.up?n.up.test(e):!0}function Ww(t,e){let n=$t[t];return n?Xs(t,e)?!1:n.up.test(e)&&zs(t,e)==="idle":!0}function Xs(t,e){let n=$t[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="cursor"?!n.up.test(e):!0:!1:!1}async function Sl(t,e,n){let r=Date.now()+n,o=0;for(await Fe(t).catch(()=>{});;){let s=await pt(t);if(Ww(e,s)){await he(lr);return}if(e==="cursor"&&Nw.test(s)&&!$t.cursor.up.test(s)&&o<3){if(!xl(t,"cursor-trust")){await he(Js);continue}o+=1,await L({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await yt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await he(lr);continue}if(e==="codex"&&o<3){let a=Dw(s);if(a){if(!xl(t,`codex-update:${a.from}->${a.to}`)){await he(Js);continue}o+=1,await L({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await yt(t,"3",{enter:!1,kind:"codex-update-skip"}),kl({sessionName:t,from:a.from,to:a.to}),await he(lr);continue}}if(Date.now()>r){if(Xs(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=$t.codex.auth.test(s)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":e==="claude"&&$t.claude.auth.test(s)&&(a="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again"),L({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),L({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!$t[e]?.block?.test(s),paneUp:!!$t[e]?.up?.test(s),paneBusy:!!$t[e]?.busy?.test(s)}).catch(()=>{}),await he(lr);return}await he(Js)}}function Tl({agent:t,agentCmd:e}={}){let n=e?Sn(e):Sn(t||"");return{label:n,realClaude:e?xn(e):n==="claude",realCodex:e?/codex/.test(e)&&!/fake-codex|fake-lead/.test(e):n==="codex",realCursor:e?/cursor/.test(e)&&!/fake-cursor|fake-box/.test(e):n==="cursor"}}function co({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Tl({agent:n,agentCmd:r});return!e||!o&&!s?null:we({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function we({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=Pw()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Tl({agent:r,agentCmd:o});if(i&&t&&await pe(t),c&&t&&await yn(t),l&&t&&await bn(t),(i||c||l)&&n){await Sl(n,a,s);return}let u=$t[r]?r:$t[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Sl(n,u,s);return}await he(lr)}var qw=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function re(t){return t==="codex"?{enter:!0,submitDelayMs:qw}:{enter:!0}}var Al=".bostrat/handoff",_l="latest.md",El="Checkpoint: write a work-in-progress handoff",Il="HANDOFF WRITTEN",Rl="Resume from .bostrat/handoff/latest.md",Kw="/clear",Vw=t=>`${El} to the EXACT absolute path ${t} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file) so a FRESH Claude context with none of this conversation can resume seamlessly. Use these sections: Objective; Done (with file paths); In progress (exact state); Next steps (ordered); Key files & commands; Open decisions/blockers; Resume instruction (one line). Be concrete and self-contained \u2014 assume the reader has none of this chat. When the file is written, reply only with ${Il} and take no further action.`,Jw=`${Rl}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`;function zw(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Il:n.startsWith(El)||n===Kw||n.startsWith(Rl):!1}var Xw=6e5;function $l(t){let e=[];for(let r of t?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-Xw;e.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&e.push({box:r.to,from:s,to:o})}let n=t?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&e.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&e.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),e}function Ol(t,e){let n=[];for(let r of t?.boxes||[]){let o=e?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function Cl(t,{windows:e=[]}={}){if(zw(t.role,t.text))return!0;if(t.role!=="assistant"||!t.ts)return!1;for(let n of e)if(t.box===n.box&&t.ts>=n.from&&t.ts<=n.to)return!0;return!1}var Yw=3500,Zw=3e5,Ys=new Map;function uo(t){return Ys.get(t)||null}async function Ml(t){let e=Ys.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await G(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);let r={name:t,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:lo.join(n.cwd,Al,_l)};return Ys.set(t,r),Qw(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function Qw(t,e){t.step="summarize",await Pl(t.name,e),t.step="clear";let n=await G(t.name);await Qs(t.name,n,"/clear"),await Bl(Yw),t.step="resume",await Qs(t.name,n,Jw),t.step="done",t.finishedAt=Date.now()}async function Pl(t,e){let n=lo.join(e,Al),r=lo.join(n,_l);await ta.mkdir(n,{recursive:!0});let o=await Zs(r),s=await G(t);return await L({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await Qs(t,s,Vw(r)),await tg(r,o,Zw),await L({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await Zs(r)}),await eg(n,r),r}async function Nl(t){let e=await G(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return Pl(t,e.cwd)}async function Zs(t){try{return(await ta.stat(t)).mtimeMs}catch{return 0}}async function tg(t,e,n){let r=Date.now()+n;for(;;){if(await Zs(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Bl(2e3)}}async function eg(t,e){try{if(!Gw.existsSync(e))return;let n=new Date().toISOString().replace(/[:.]/g,"-");await ta.copyFile(e,lo.join(t,`${n}.md`))}catch{}}function Bl(t){return new Promise(e=>setTimeout(e,t))}async function Qs(t,e,n){if(e){let o=e.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await we({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await G(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await yt(t,n,{kind:"orchestration-drive",...re(r.agent)})}import P from"node:fs/promises";import U from"node:path";import{randomUUID as Qo}from"node:crypto";import{execFile as Dx}from"node:child_process";import{promisify as jx}from"node:util";import ea from"node:fs/promises";import ng from"node:path";var rg=0;async function qt(t,e,{mode:n}={}){await ea.mkdir(ng.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++rg}.tmp`;await ea.writeFile(r,JSON.stringify(e,null,2)+`
24
- `,n!=null?{mode:n}:void 0),await ea.rename(r,t)}import{execFile as og}from"node:child_process";import{promisify as sg}from"node:util";import{randomUUID as ag}from"node:crypto";var He=sg(og),fo=t=>new Promise(e=>setTimeout(e,t)),ig=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Dl=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,cg=250,lg=400,jl={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}};async function Ul(t){let e=await sl(t);if(!e)return!1;try{return await He("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function na(t,e){let n=Date.now()+e;for(;;){if(await Ul(t))return!0;if(Date.now()>n)return!1;await fo(cg)}}async function ug(t,e){let n=jl[e]||jl.default;n.escape&&(await gt(t,["Escape"],{kind:"swap-exit"}),await fo(150)),n.wipe&&await no(t,400,{kind:"swap-exit"}),await ro(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=re(e).submitDelayMs||0;await fo(Math.max(r,250)),await gt(t,["Enter"],{kind:"swap-exit"})}async function dg(t,e){try{let{stdout:n}=await He(O,C("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Ll(t){await He(O,C("respawn-pane","-k","-t",t))}async function ra(t,e={}){if(!ut(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await G(t);if(!r)throw new Error(`session "${t}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${t}"`);let o=r.agent||"agent",s=e.agent||Sn(n),a=Number(e.exitTimeoutMs)||ig;await L({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Ul(t)&&(await ug(t,o),!await na(t,a)&&(i=!0,await L({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await Ll(t),!await na(t,Dl))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&xn(n),l=c?Zr():null,u=await dg(t,"ANTHROPIC_API_KEY"),d=!1;try{l?(await He(O,C("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await He(O,C("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),d=!0)}catch(x){throw l?Ms(x,l):x}if(d&&!i&&(await Ll(t),!await na(t,Dl)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,w=e.resumeSessionId||ag(),m=f?n:Bs(n,w),g=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await pe(r.cwd),s==="codex"&&await yn(r.cwd),s==="cursor"&&await bn(r.cwd),await Fe(t),await He(O,C("set-option","-t",t,"@bostrat-sid",w)),await He(O,C("set-option","-t",t,"@bostrat-agent",s)),await fo(lg),await yt(t,m,{kind:"swap-launch"}),await L({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:w,forced:i,envRespawned:d}),{name:t,cwd:r.cwd,sessionId:w,agent:s,pinned:g,forced:i,envRespawned:d}}import Sg from"node:fs";import Hl from"node:fs/promises";import dr from"node:path";import vg from"node:os";import{fileURLToPath as Tg}from"node:url";import{execFile as Ag}from"node:child_process";import{promisify as _g}from"node:util";import sa from"node:fs/promises";import ge from"node:path";import{execFile as fg}from"node:child_process";import{promisify as pg}from"node:util";var mg=pg(fg),Fl=process.env.HOME||"/home/globo",hg=ge.join(process.env.BOSTRAT_STATE_DIR||ge.join(Fl,".bostrat"),"repos"),wg=[...(process.env.BOSTRAT_REPOS_ROOT||ge.join(Fl,"Documents")).split(","),hg].map(t=>t.trim()).filter(Boolean),gg=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,yg=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function bg(t){try{return(await sa.stat(ge.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function kg(t){let e=i=>sa.readFile(ge.join(t,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await e("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await e(`refs/heads/${i}`)||(o??=await e("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await e("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function oe(){let t=new Map;async function e(n,r){if(t.has(n))return;let o=await bg(n);if(o==="worktree")return;if(o==="repo"){t.set(n,{path:n,name:ge.basename(n),branch:await kg(n)});return}if(r>=gg)return;let s;try{s=await sa.readdir(n,{withFileTypes:!0})}catch{return}for(let a of s)!a.isDirectory()||a.name.startsWith(".")||yg.has(a.name)||await e(ge.join(n,a.name),r+1)}for(let n of wg)await e(ge.resolve(n),0);return[...t.values()].sort((n,r)=>n.name.localeCompare(r.name))}function xg(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var oa=new Map;async function Y(t){if(!t)return null;if(oa.has(t))return oa.get(t);let e=null;try{let{stdout:n}=await mg("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=xg(n)}catch{e=null}return oa.set(t,e),e}var Wl=_g(Ag),Eg=process.env.BOSTRAT_STATE_DIR||dr.join(process.env.HOME||vg.tmpdir(),".bostrat"),Ig=dr.join(Eg,"repos"),ql=/^[^/\s]+\/[^/\s]+$/,Rg=5*6e4,$g=6e4,Og=10*6e4,Gl=fetch,aa=new Map,We=null;function ia(t=process.env){let e=t.BOSTRAT_PLANE_URL||"";if(!e)return null;try{let n=new URL(e);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function po(t=process.env){return!!(ia(t)&&t.BOSTRAT_PLANE_TOKEN)}var Kl=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),An=(t,e)=>Object.assign(new Error(t),{status:e});async function mo(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!ql.test(n))throw An("repo must be owner/name",400);let r=aa.get(n);if(r&&r.expiresAt-Date.now()>Rg)return r;if(!po(e))throw An("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Gl(`${ia(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Kl(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw An(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if(o.status>=500&&r&&r.expiresAt>Date.now())return r;throw An(s?.error||`plane replied ${o.status}`,o.status)}let a={token:s.token,expiresAt:Number(s.expiresAt)||Date.now()+55*6e4,username:s.username??null,installationId:s.installationId??null};return aa.set(n,a),a}async function fr({env:t=process.env,force:e=!1}={}){if(!po(t))return{configured:!1,repos:[],username:null};if(!e&&We&&Date.now()-We.at<$g)return We.data;try{let n=await Gl(`${ia(t)}/v1/github/repos`,{headers:Kl(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return We={at:Date.now(),data:r},r}catch(n){return We?We.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Vl(){aa.clear(),We=null}async function Gt({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await Y(t):null);if(!r)return{};let{token:o}=await mo(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function ca({env:t=process.env}={}){return(await fr({env:t})).username??null}var Cg=()=>Tg(new URL("../bin/bostrat-git-credential",import.meta.url)),Mg=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Pg(t){let[e,n]=String(t).split("/");return dr.join(Ig,e,n)}async function la(t,{env:e=process.env}={}){let n=(...r)=>Wl("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Cg()}" ${Mg(e)}`),await n("config","credential.useHttpPath","true")}async function ua(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!ql.test(String(t||"")))throw An(`bad repo slug "${t}"`,400);let r=Pg(t);if(Sg.existsSync(dr.join(r,".git")))return r;let{token:o}=await mo(t,{env:e});await Hl.mkdir(dr.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Wl("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:Og,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Hl.rm(r,{recursive:!0,force:!0}).catch(()=>{}),An(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await la(r,{env:e}),r}import py from"node:fs/promises";function Ng(t){let e=[],n="",r=null;for(let o=0;o<t.length;o++){let s=t[o];if(r){n+=s,s===r&&t[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&t[o+1]==="&"){e.push(n),n="",o++;continue}if(s==="|"&&t[o+1]==="|"){e.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
25
- `){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function pr(t){let e=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}e.push(n),n="",o=!1}};for(let a=0;a<t.length;a++){let i=t[a];if(r){i===r&&t[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&t[a+1]==="<")return s(),e.push("<<"),e;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),e}function mr(t){let e=t;for(;e.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(e[0]);)e=e.slice(1);for(;e.length&&(e[0]==="sudo"||e[0]==="time"||e[0]==="env");)e=e.slice(1);return e}var En=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,kt=t=>t&&!/[$`]/.test(t)?t:null,st=t=>t.replace(/\/+$/,"").split("/").pop()||t,_n=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},Xl=t=>t.startsWith("-"),Jl=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function V(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(Xl(o)){e.has(o)&&r++;continue}n.push(o)}return n}var Bg={status:()=>"Check git status",diff:()=>"Review changes",log:()=>"Review commit history",show:()=>"Inspect a commit",add:()=>"Stage files",commit:()=>"Commit changes",push:()=>"Push to remote",pull:()=>"Pull from remote",fetch:()=>"Fetch from remote",merge:()=>"Merge branches",rebase:()=>"Rebase branch",stash:()=>"Stash changes",clone:t=>t[1]?`Clone ${st(t[1])}`:"Clone repository",checkout:()=>"Switch branches",switch:()=>"Switch branches",restore:()=>"Restore files",reset:()=>"Reset git state",branch:(t,e)=>e.includes("--show-current")?"Check current branch":"List branches",remote:()=>"Check git remotes","rev-parse":()=>"Resolve a git ref",worktree:()=>"Manage git worktrees","merge-base":()=>"Find the merge base","ls-files":()=>"List tracked files"},Dg={pr:t=>({create:"Create a pull request",list:"List pull requests",view:"View a pull request",merge:"Merge a pull request",checks:"Check PR status"})[t[1]]||"Run a gh pr command",auth:()=>"Authenticate GitHub CLI",api:()=>"Call the GitHub API",repo:()=>"Inspect the GitHub repo",issue:()=>"Work with GitHub issues"};function Yl(t){let e=t.indexOf("-n"),n=e!==-1?t[e+1]:null;if(!n)return null;let r=/^(\d+),(\d+)p$/.exec(n)||/^(\d+)p$/.exec(n);return r?r[2]?{from:r[1],to:r[2]}:{from:r[1],to:r[1]}:null}function jg(t){let e=Yl(t),n=kt(V(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${st(n)}`:`Read lines ${e.from}\u2013${e.to} of ${st(n)}`:t.includes("-i")&&n?`Edit ${st(n)}`:null}function da(t){if(t.includes("--files")){let s=kt(V(t,Jl)[0]);return s&&s!=="."?`List files in ${_n(s)}`:"List files"}let e=V(t,Jl),n=e[0];if(!n)return null;let r=kt(e.slice(1).find(s=>s!==".")),o=En(n,40);return r?`Search for \u201C${o}\u201D in ${_n(r)}`:`Search for \u201C${o}\u201D`}function fa(t){let e=V(t,new Set(["--prefix"])),n=e[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?e[1]?`Run ${e[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function pa(t,e){if(t.includes("-e")||t.includes("--eval")||t.includes("-c")||t[1]==="-"||t.includes("<<"))return`Run an inline ${e} script`;if(t.includes("--test"))return"Run tests";let n=V(t)[0];return n?`Run ${st(n)}`:null}function Lg(t){let e=V(t),n=e[0],r=e.find(a=>/\.(service|timer|socket|target)$/.test(a))||e[1],o=r?r.replace(/\.service$/,""):"a service";return{status:`Check ${o} status`,show:`Inspect ${o}`,restart:`Restart ${o}`,start:`Start ${o}`,stop:`Stop ${o}`,"daemon-reload":"Reload systemd"}[n]||null}function zl(t){return t.find(e=>/^https?:\/\//.test(e))||null}var Ug={git:t=>{let e=V(t,new Set(["-C","-c"])),n=Bg[e[0]];return n?n(e,t):null},gh:t=>{let e=V(t),n=Dg[e[0]];return n?n(e):null},sed:jg,rg:da,grep:da,egrep:da,nl:t=>{let e=kt(V(t)[0]);return e?`Read ${st(e)}`:null},cat:t=>{let e=kt(V(t)[0]);return e?`Read ${st(e)}`:null},head:t=>{let e=kt(V(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${st(e)}`:null},tail:t=>{let e=kt(V(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${st(e)}`:null},wc:t=>{let e=kt(V(t)[0]);return e?`Count lines in ${st(e)}`:"Count input lines"},ls:t=>{let e=kt(V(t)[0]);return e&&e!=="."?`List files in ${_n(e)}`:"List files"},find:t=>kt(t[1])&&!Xl(t[1])&&t[1]!=="."?`Find files in ${_n(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=kt(V(t)[0]);return e?`Create directory ${_n(e)}`:null},touch:t=>{let e=kt(V(t)[0]);return e?`Create ${st(e)}`:null},rm:t=>{let e=kt(V(t)[0]);return e?`Delete ${_n(e)}`:null},mv:t=>{let e=kt(V(t)[0]);return e?`Move ${st(e)}`:null},cp:t=>{let e=kt(V(t)[0]);return e?`Copy ${st(e)}`:null},chmod:t=>{let e=kt(V(t)[1]);return e?`Change permissions on ${st(e)}`:null},diff:t=>{let e=V(t);return e.length>=2?`Compare ${st(e[0])} and ${st(e[1])}`:null},curl:t=>{let e=zl(t);return e?`Fetch ${En(e,56)}`:"Fetch a URL"},wget:t=>{let e=zl(t);return e?`Fetch ${En(e,56)}`:"Fetch a URL"},npm:fa,pnpm:fa,yarn:fa,npx:t=>{let e=V(t)[0];return e?`Run ${e}`:null},node:t=>pa(t,"Node"),python:t=>pa(t,"Python"),python3:t=>pa(t,"Python"),make:t=>{let e=V(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:Lg,journalctl:t=>{let e=t.findIndex(r=>r==="-u"||r==="--unit"||r==="--user-unit"),n=e!==-1?t[e+1]:null;return n?`Read ${n.replace(/\.service$/,"")} logs`:"Read system logs"},tmux:t=>{let e=V(t,new Set(["-S","-L","-t","-f"]))[0];return{ls:"List tmux sessions","list-sessions":"List tmux sessions","kill-session":"Kill a tmux session","send-keys":"Send keys to a tmux session","capture-pane":"Capture a tmux pane","new-session":"Start a tmux session"}[e]||null},ps:()=>"List processes",which:t=>{let e=V(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=V(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},Fg=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function Hg(t){let e=mr(pr(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(st(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return ma(e[2]);let n=st(e[0]);if(Fg.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=Ug[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function ma(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
26
- `,n);i!==-1&&(e=e.slice(0,i))}let r=Ng(e);for(let i=0;i<r.length-1;i++){let c=mr(pr(r[i])),l=mr(pr(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(st(c[0]))&&st(l[0])==="sed"){let u=Yl(l),d=V(c)[0];if(u&&d)return En(u.from===u.to?`Read line ${u.from} of ${st(d)}`:`Read lines ${u.from}\u2013${u.to} of ${st(d)}`,88)}}let o=[];for(let i of r){let c=Hg(i);c&&o.push({d:c,head:st(mr(pr(i))[0]||""),sub:V(mr(pr(i)))[0]})}if(!o.length)return null;let s=o.find(i=>i.head==="git"&&i.sub==="commit"),a=o.find(i=>i.head==="git"&&i.sub==="push");return s&&a?"Commit and push changes":En(s?s.d:o[0].d,88)}var wa=new Set(["Bash"]);function In(t){let e=t.split(`
27
- `).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function ye(t,e=""){let n=e.trim();return n||ma(t)||In(t)}function ha(t,e){if(t){if(Array.isArray(t)){for(let n of t)ha(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&wa.has(t.name)){let n=String(t.input?.command||"").trim();n&&e.push({id:t.id||null,tool:t.name,command:n,description:String(t.input?.description||"").trim()})}for(let n of Object.values(t))ha(n,e)}}}async function Zl(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=[];for(let s of r.split(`
28
- `)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(ha(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:ye(d.command,d.description),ts:u})}return o.slice(-n)}function Ql(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,g)=>g>=s&&(m.ts||0)>(l.ts||0)),d=u===-1?r.length:u,f=r.slice(s,d);if(s=d,!f.length)continue;let w=f.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:w,ts:Math.max(...w.map(m=>m.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var Wg=/^Async (?:agent|task) launched/i,qg=t=>{if(typeof t=="string")return t.trim();if(!Array.isArray(t))return"";let e=t.find(n=>n&&n.type==="text"&&typeof n.text=="string");return e?e.text.trim():""};async function tu(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
29
- `)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let w=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(w.description||"").trim()||null,detail:String(w.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let w=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(w?In(w):null),detail:w?In(w):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&Wg.test(qg(f.content))&&s.add(f.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}import hr from"node:fs";import Kg from"node:fs/promises";import qe from"node:path";import Vg from"node:os";import Jg from"node:crypto";import zg from"node:zlib";var ho=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),jt=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Rn=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function be(t,e){if(!t)return null;let n=String(t);if(e){let o=e.endsWith("/")?e:`${e}/`;if(n===e)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}var ga=t=>`"${String(t).replace(/"/g,'\\"')}"`;function Gg(t){let e=typeof t=="string"?t:String(t?.patch??t?.input??""),n=[];for(let r of e.matchAll(/^\*{3} (?:Update|Add|Delete) File: (.+)$/gm))n.push(r[1].trim());return n}function eu(t,e,n){switch(t){case"Shell":{let r=String(e?.command||"").trim();return r?{command:r,description:String(e?.description||"").trim()}:null}case"Read":{let r=e?.path;if(!r)return null;let o=Number(e?.offset)||null,s=o&&Number(e?.limit)?o+Number(e.limit)-1:null;return{command:jt(`read ${be(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Rn(r)}`:`Read ${Rn(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?be(e.path,n):null,s=e?.glob?` --glob ${ga(e.glob)}`:"",a=jt(String(r),40);return{command:jt(`grep ${ga(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=e?.glob_pattern;if(!r)return null;let o=e?.target_directory?be(e.target_directory,n):null;return{command:jt(`glob ${ga(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${jt(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:jt(`edit ${be(e.path,n)}`,140),description:`Edit ${Rn(e.path)}`}:null;case"Write":return e?.path?{command:jt(`write ${be(e.path,n)}`,140),description:`Write ${Rn(e.path)}`}:null;case"Delete":return e?.path?{command:jt(`delete ${be(e.path,n)}`,140),description:`Delete ${Rn(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>be(s,n)).filter(Boolean);return{command:jt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=Gg(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:jt(`apply_patch ${be(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Rn(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:jt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Xg=process.env.BOSTRAT_CURSOR_CHATS||qe.join(Vg.homedir(),".cursor","chats");function ou(t){let e=Jg.createHash("md5").update(String(t)).digest("hex");return qe.join(Xg,e)}function su(t){try{let e=JSON.parse(hr.readFileSync(qe.join(t,"meta.json"),"utf8"));return{createdAtMs:Number(e.createdAtMs)||null,updatedAtMs:Number(e.updatedAtMs)||Number(e.createdAtMs)||null,hasConversation:e.hasConversation===!0}}catch{return null}}function Yg(t){let e=ou(t),n;try{n=hr.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=qe.join(e,o.name),a=qe.join(s,"store.db");if(!hr.existsSync(a))continue;let i=su(s);if(!i)continue;let c={id:o.name,dir:s,store:a,...i};(!r||c.hasConversation&&!r.hasConversation||c.hasConversation===r.hasConversation&&(c.updatedAtMs||0)>(r.updatedAtMs||0))&&(r=c)}return r}function wo(t,e){if(!t)return null;if(e){let n=qe.join(ou(t),e),r=qe.join(n,"store.db");if(hr.existsSync(r))return{id:e,dir:n,store:r,...su(n)||{createdAtMs:null,updatedAtMs:null}}}return Yg(t)}function nu(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function Zg(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=zg.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function Qg(t){let e=[],n=0,r=t.length,o=()=>{let s=0,a=0;for(;n<r;){let i=t[n++];if(s+=(i&127)*2**a,!(i&128))break;a+=7}return s};for(;n<r;){let s=o(),a=s>>>3,i=s&7;if(i===2){let c=o(),l=t.subarray(n,n+c);n+=c,a===1&&c===32&&e.push(l.toString("hex"))}else if(i===0)o();else if(i===5)n+=4;else if(i===1)n+=8;else break}return e}function ty(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!ho.has(r.toolName))continue;let o=eu(r.toolName,r.args,e);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}var ey=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
30
- `).trim():"",ny=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],ru=process.getBuiltinModule?.("node:sqlite"),$n={turns:[],trailingCommands:[],model:null,requestIds:[]};async function ya(t,e){if(!ru)return $n;let{DatabaseSync:n}=ru,r;try{r=new n(t,{readOnly:!0})}catch{return $n}try{let o=r.prepare("SELECT value FROM meta WHERE key = '0'").get()||r.prepare("SELECT value FROM meta LIMIT 1").get();if(!o)return $n;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return $n}let a=s.lastUsedModel||null,i=s.latestRootBlobId?nu(r,s.latestRootBlobId):null;if(!i)return{...$n,model:a};let c=[],l=[],u=[],d=null,f=[];for(let w of Qg(i)){let m=Zg(nu(r,w));if(!m)continue;let g=m.providerOptions?.cursor;if(g?.requestId&&f.push(g.requestId),g?.modelProviderMessageId&&f.push(g.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let S of m.content){let T=S?.providerOptions?.cursor?.modelName;T&&(d=T)}l.push(...ty(m.content,e).map((S,T)=>({...S,id:S.id||`${w}:${T}`}))),u.push(...ny(m.content));let x=ey(m.content);if(!x)continue;let A=[...new Set(u)];u=[],c.push({id:w,text:x,tools:A.length?A:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return $n}finally{try{r.close()}catch{}}}function au(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(hr.statSync(t.store).mtimeMs)}catch{n=Date.now()}let r=t.createdAtMs;(!r||r>n)&&(r=n-e*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/e)}}async function iu(t,e,{limit:n=80}={}){let r=wo(t,e);if(!r)return[];let{turns:o,model:s}=await ya(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=au(r,a.length);return a.map((c,l)=>({role:"assistant",text:c.text,tools:c.tools,ts:i(l),model:c.model||s,sourceId:c.id}))}async function cu(t,e){let n=wo(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await ya(n.store,t);return{chatId:n.id,requestIds:o,models:[...new Set(r.map(a=>a.model).filter(Boolean))],configuredModel:s,updatedAtMs:n.updatedAtMs||null}}async function lu(t,e,{limit:n=80}={}){let r=wo(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await ya(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=au(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:ye(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:ye(u.command,u.description),ts:i+1});return l}async function uu(t,e){let n=wo(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await Kg.stat(n.store)).mtimeMs)}catch{return null}}import du from"node:fs";import ka from"node:fs/promises";import ba from"node:path";import ry from"node:os";var oy=process.env.BOSTRAT_CODEX_SESSIONS||ba.join(process.env.CODEX_HOME||ba.join(ry.homedir(),".codex"),"sessions");async function fu(t){try{return await ka.realpath(t)}catch{return t}}function sy(t=oy){let e=[],n=r=>{let o;try{o=du.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=ba.join(r,s.name);if(s.isDirectory())n(a);else if(s.isFile()&&s.name.startsWith("rollout-")&&s.name.endsWith(".jsonl")){let i=/-([0-9a-f-]{36})\.jsonl$/i.exec(s.name);try{e.push({id:i?i[1]:null,file:a,mtimeMs:du.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function ay(t,e=1048576){let n;try{n=await ka.open(t,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(e,65536)),o="",s=0;for(;;){let{bytesRead:a}=await n.read(r,0,r.length,s);if(!a)break;o+=r.toString("utf8",0,a);let i=o.indexOf(`
31
- `);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function iy(t){let e=await ay(t);if(!e)return null;let n;try{n=JSON.parse(e)}catch{return null}return n?.type==="session_meta"&&n?.payload?.cwd||null}async function Ge(t,e){if(!t&&!e)return null;let n=sy();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await fu(t);for(let o of n){let s=await iy(o.file);if(s&&(s===t||await fu(s)===r))return o}return null}var cy=new Set(["output_text","text"]),ly=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&cy.has(e.type)).map(e=>e.text).join(`
32
- `).trim():"";async function pu(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Ge(t,e);if(!o)return[];let s;try{s=await vt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let f of s.split(`
33
- `)){let w=f.trim();if(!w||w[0]!=="{")continue;let m;try{m=JSON.parse(w)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let S=m.payload?.type;S==="task_started"?(l=!0,u=!1,d=m.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Sa,sourceId:m.payload?.turn_id||d||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let g=m.payload;if(!g)continue;if((g.type==="function_call"||g.type==="custom_tool_call")&&g.name){u=!0,xa.has(g.name)||c.push(g.name);continue}if(g.type!=="message"||g.role!=="assistant")continue;u=!0;let x=ly(g.content),A=c.length?c.slice():null;c=[],!(!x&&!A)&&a.push({role:"assistant",text:x,tools:A||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let f of a)f.model||(f.model=i);return a.slice(-n)}var wr=new Set(["exec_command","shell","exec"]),xa=new Set(["wait","write_stdin"]),Sa="Codex ended the turn without a response \u2014 it hit an error the CLI shows only in the terminal (e.g. quota or billing). Check the Terminal tab.";function uy(t){if(typeof t?.cmd=="string"&&t.cmd.trim())return t.cmd.trim();let e=t?.command;return typeof e=="string"&&e.trim()?e.trim():Array.isArray(e)&&e.length?e.length===3&&/^(ba)?sh$/.test(e[0])&&e[1]==="-lc"?String(e[2]).trim():e.map(String).join(" ").trim():null}function dy(t){let e=t[0];if(e!=='"'&&e!=="'"&&e!=="`")return null;let n="";for(let r=1;r<t.length;r++){let o=t[r];if(o==="\\"){let s=t[++r];if(s===void 0)return null;if(s==="n")n+=`
34
- `;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function fy(t){let e=String(t||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(e);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=e.slice(s,a+1<n.length?n[a+1]:e.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=dy(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function mu(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Ge(t,e);if(!o)return[];let s;try{s=await vt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
35
- `)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&wr.has(u.name)){let w;try{w=JSON.parse(u.arguments||"{}")}catch{continue}let m=uy(w);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=fy(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((w,m)=>{a.push({id:f?m?`${f}:${m}`:f:null,tool:u.name,command:w,description:"",summary:ye(w),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function hu(t,e){let n=await Ge(t,e);if(!n)return null;try{return Math.round((await ka.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var On=class{constructor(e,n=new Set){this.agent=e,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},va=class extends On{transcript(e){return!e?.cwd||!e?.sessionId?null:ne(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Gs(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?Zl(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?tu(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await py.stat(n)).mtimeMs)}catch{return null}}},Ta=class extends On{async readMessages(e,n){return iu(e.cwd,e.sessionId,n)}async readCommands(e,n){return lu(e.cwd,e.sessionId,n)}async updatedAt(e){return uu(e.cwd,e.sessionId)}},Aa=class extends On{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return pu(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return mu(e.cwd,this.sessionId(e),n)}async updatedAt(e){return hu(e.cwd,this.sessionId(e))}},my=new On("unknown"),wu=new Map;function _a(t){return wu.set(t.agent,t),t}function gr(t){return wu.get(t)||my}_a(new va("claude",wa));_a(new Ta("cursor",ho));_a(new Aa("codex",wr));var Ea=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function gu(t,e={}){return(await gr(t.agent).readMessages(t,e)).map(Ea(t))}async function yu(t,e={}){return(await gr(t.agent).readCommands(t,e)).map(Ea(t))}async function bu(t,e={}){return(await gr(t.agent).readJobs(t,e)).map(Ea(t))}var hy=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,ku=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function wy(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
36
- `);if(!ku.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(hy);if(u)o.unshift({n:Number(u[1]),label:u[2]}),s=!0;else{if(s&&r[l].trim()==="")continue;if(s){var a=r[l].trim();break}}}if(o.length<2)return null;let i=o.sort((l,u)=>l.n-u.n);if(i.some((l,u)=>l.n!==u+1))return null;let c=typeof a=="string"&&ku.test(a)?a:"The agent is asking for approval";return{provider:e,box:n,promptId:`${e}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}var go=/(^|\n)\s*›/,xu=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function gy(t,e=""){let n=String(t);if(!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n)||go.test(n))return null;let r=n.replace(/\r/g,"").split(`
37
- `),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(xu);if(!u)continue;s<0&&(s=l);let d="";for(let f=l+1;f<Math.min(l+3,r.length);f++){let w=r[f].trim();if(w){if(xu.test(r[f])||/^Press enter/.test(w))break;d=w;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!/Welcome to Codex/.test(u)&&i.push(u)}let c=i.join(" ")||"Codex isn't signed in on this box \u2014 choose a sign-in method";return{provider:"codex",box:e,multiSelect:!1,cursorIndex:Math.max(0,a.findIndex(l=>l.cursor)),answerMode:"keys",promptId:`codex:signin:${a.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Sign in",multiSelect:!1,options:a.map(l=>({label:l.label,description:l.desc}))}]}}var Su=/Enter this one-time code/,yy=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function by(t,e=""){let n=String(t);if(!Su.test(n)||go.test(n))return null;let r=n.replace(/\r/g,"").split(`
38
- `),o=r.findIndex(i=>Su.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(yy);if(c){s=c[1];break}}if(!s)return null;let a=(n.match(/https:\/\/\S+/)||[])[0]||"https://auth.openai.com/codex/device";return{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:`codex:device-code:${s}`,questions:[{question:`Codex is waiting for you to finish signing in: open ${a} and enter code ${s} (expires in 15 minutes). The code goes straight to OpenAI \u2014 bostrat never sees your login. This card clears once OpenAI confirms.`,header:"Sign in",multiSelect:!1,options:[{label:"Cancel sign-in",description:"Go back to the sign-in options"}]}]}}function ky(t,e=""){let n=String(t);return!/open the following link to authenticate/.test(n)||go.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-browser",questions:[{question:"Codex started its browser sign-in, which opens on the box itself and can't complete from another device. Go back and choose \u201CSign in with Device Code\u201D instead.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function xy(t,e=""){let n=String(t);return!/Paste or type your API key/.test(n)||go.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-apikey",questions:[{question:"Codex is asking for an OpenAI API key. For safety a key can only be typed in the Terminal tab, never through chat. To use your ChatGPT subscription instead, go back and pick a sign-in option.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function vu(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=by(e,n)||ky(e,n)||xy(e,n)||gy(e,n);if(r)return r}return wy(e,{provider:t,box:n})}function Tu(t,e){let n=Array.isArray(e?.[0])?e[0]:[],o=(Number.isInteger(n[0])&&n[0]>=0?n[0]:0)+1;return String(o)}var Eu=/to navigate\b.*\bEsc to cancel|Enter to select\b/,$a=/Chat about this/i,Ke=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Au=/^(type something|chat about this|submit)\b/i,Ve=/^[─—-]{6,}\s*$/,Oa=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function Je(t){let e=t.search(Oa);return(e>=0?t.slice(0,e):t).trim()}function Sy(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function vy(t){let e=String(t).trim();return!!(!e||Ve.test(t)||Oa.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function Ty(t,e=""){let n=[],r=/([☐☒])\s+([^☐☒←→✔]+)/g,o;for(;(o=r.exec(t))!==null;)n.push({label:o[2].trim(),answered:o[1]==="\u2612",current:!1});if(!n.length)return null;let s=Ay(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function Ay(t,e){let n=String(e).toLowerCase();if(n){let o=t.map((s,a)=>({i:a,label:s.label.toLowerCase()})).sort((s,a)=>a.label.length-s.label.length);for(let{i:s,label:a}of o){if(a.split(/\s+/).filter(l=>l.length>3).some(l=>n.includes(l)))return s;let c=a.slice(0,8).trim();if(c.length>=4&&n.includes(c))return s}}let r=t.findIndex(o=>!o.answered);return r>=0?r:t.length-1}function _u(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Iu=/^\s*Switch model\?\s*$/m;function _y(t,e=""){let n=String(t);if(!Iu.test(n))return null;let r=n.replace(/\r/g,"").split(`
39
- `),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let d=r[u].match(Ke);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!Ve.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:Je(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}var Ru=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,Ey=/^\s*[─—▔-]{6,}\s*$/;function Iy(t,e=""){let n=String(t).replace(/\r/g,"").split(`
40
- `),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Ru.test(n[r]))return null;let o=r-1;for(;o>=0&&n[o].trim()==="";)o--;let s=[];for(;o>=0;o--){let c=n[o].match(Ke);if(!c)break;s.unshift({cursor:!!c[1],n:Number(c[2]),label:c[4].trim()})}if(s.length<2||s.some((c,l)=>c.n!==l+1))return null;let a=[];for(let c=o;c>=0&&a.length<6&&!(Ey.test(n[c])||Ve.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Je(c.label),description:""}));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(c=>c.cursor)),promptId:`claude:confirm-gate:${i.map(c=>c.label).join("|")}`.slice(0,240),questions:[{question:a.join(" ")||"Claude Code is asking you to confirm",header:"Confirm",multiSelect:!1,options:i}]}}function Ry(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
41
- `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Ke);l&&o.push({cursor:!!l[1],label:l[4].trim()})}let s=o.filter(c=>/^(submit answers|cancel)$/i.test(c.label));if(s.length<2)return null;let a=s.map(c=>({label:c.label,description:""})),i=Math.max(0,s.findIndex(c=>c.cursor));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}var Ia=/Resume from summary/i,$y=/^\s*This session is\b/;function Oy(t,e=""){let n=String(t);if(!Ia.test(n))return null;let r=n.replace(/\r/g,"").split(`
42
- `),o=-1;for(let u=r.length-1;u>=0;u--)if($y.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let d=r[u].match(Ke);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!Ve.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>Ia.test(u.label)))return null;let i=s.map(u=>({label:Je(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}var ze=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function Ca(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(Ke);o&&e.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=e.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}var $u=/Choose the text style that looks best with your terminal/,Ou=/Let's get started\./;function Cy(t,e=""){let n=String(t);if(!$u.test(n)||!Ou.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
43
- `),o=Ca(r);return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:Je(s.label),description:""}))}]}:null}var Cu=/Select login method:/;function My(t,e=""){let n=String(t);if(!Cu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
44
- `),o=Ca(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Je(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}var Ma=/Paste code here if prompted/,Py=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function Ny(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!Py.test(o)||Ma.test(t[r]))break;n+=o}return n}function By(t,e=""){let n=String(t);if(!Ma.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
45
- `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=Ny(r,s);break}return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,answerMode:"text",setup:"oauth",url:o,inputLabel:"Paste your sign-in code",promptId:"claude:setup:oauth",questions:[{question:"Open the sign-in link, approve access, then paste the code you're shown back here. The code goes straight to the Claude CLI on your box \u2014 bostrat relays the keystrokes and never stores it.",header:"Log in",multiSelect:!1,options:[]}]}:null}var Ra=/OAuth error:/,Dy=/Press Enter to retry\./;function jy(t,e=""){let n=String(t);if(!Ra.test(n)||!Dy.test(n)||ze.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
46
- `)].reverse().find(a=>Ra.test(a))||"OAuth error").trim();return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}var Mu=/Quick safety check: Is this a project you created or one you trust/;function Ly(t,e=""){let n=String(t);if(!Mu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
47
- `),o=Ca(r);return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:Je(s.label),description:""}))}]}:null}var Pu=/^\s*Press Enter to continue\s*$/;function Uy(t,e=""){let n=String(t);if(ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
48
- `),o=-1;for(let i=r.length-1;i>=0;i--)if(r[i].trim()!==""){o=i;break}if(o<0||!Pu.test(r[o]))return null;let s=[];for(let i=o-1;i>=0&&s.length<3;i--){let c=r[i].trim();if(!c){if(s.length)break;continue}if(Ve.test(r[i])||Oa.test(c))break;s.unshift(c)}let a=s.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${a}`.slice(0,200),questions:[{question:a,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function Fy(t){let e=-1;for(let r=t.length-1;r>=0;r--)if($a.test(t[r])){e=r;break}if(e<0)return null;let n=-1;for(let r=e-1;r>=0;r--)if(/[☐☒]/.test(t[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:e,optStart:n+1,optEnd:e}}function Hy(t,e=""){let n=String(t);if(!Eu.test(n)||!$a.test(n))return null;let r=n.replace(/\r/g,"").split(`
49
- `),o=Fy(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let h=i;h<c;h++){let p=r[h].match(Ke);p&&(u<0&&(u=h),l.push({line:h,cursor:!!p[1],checked:p[3]!=null&&p[3]!==" ",hasCheckbox:p[3]!=null,label:p[4].trim(),desc:""}))}if(!l.length)return null;for(let h of l)for(let p=h.line+1;p<Math.min(r.length,h.line+4)&&!(Ke.test(r[p])||Ve.test(r[p]));p++){let b=r[p].trim();if(!(!b||vy(r[p])||Au.test(b))){h.desc=Sy(b);break}}let d=l.filter(h=>!Au.test(h.label));if(!d.length)return null;let f=d.some(h=>h.hasCheckbox),w=Math.max(0,d.findIndex(h=>h.cursor)),m=s>=0?s+1:0,g=[];for(let h=m;h<u;h++){let p=r[h].trim();p&&!Ve.test(r[h])&&!/[☐☒]/.test(r[h])&&g.push(p)}let x=g.join(" ")||(s>=0?_u(r[s]):"")||"The agent is asking a question",A=s>=0?Ty(r[s],x):null,S=A?.currentTabLabel||(s>=0?_u(r[s]):""),T=d.map(h=>({label:Je(h.label),description:h.desc})),E=T.map(h=>h.label).join("|"),M=`claude:${A?`q${A.questionIndex+1}/${A.questionTotal}`:"q1/1"}:${S}:${x}:${E}`.slice(0,240),I=A?`claude:ask:${A.tabs.map(h=>h.label).join("|")}`.slice(0,240):M;return{provider:"claude",box:e,multiSelect:f,cursorIndex:w,promptId:M,groupId:I,...A?{questionIndex:A.questionIndex,questionTotal:A.questionTotal,currentTabLabel:A.currentTabLabel,tabs:A.tabs}:{},questions:[{question:x,header:S,multiSelect:f,options:T}]}}function Cn(t,e){let n=String(e||"");return n?t==="claude"?Eu.test(n)&&$a.test(n)||Iu.test(n)||Ia.test(n)||Ru.test(n.trimEnd().split(`
50
- `).pop()||"")?!0:ze.test(n)?!1:$u.test(n)&&Ou.test(n)||Cu.test(n)||Ma.test(n)||Ra.test(n)||Mu.test(n)||Pu.test(n.trimEnd().split(`
51
- `).pop()||""):t==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate/i.test(n)?!0:t==="cursor"||t==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function Xe(t,e){return!t||!e?null:t.agent==="claude"?Ry(e,t.name)||Hy(e,t.name)||_y(e,t.name)||Oy(e,t.name)||jy(e,t.name)||By(e,t.name)||My(e,t.name)||Cy(e,t.name)||Ly(e,t.name)||Uy(e,t.name)||Iy(e,t.name):t.agent==="cursor"||t.agent==="codex"?vu(t.agent,e,t.name):null}async function Pa(t){if(!t)return null;let e=await me(t.name).catch(()=>"");return Xe(t,e)}function Nu(t,e){let n=Array.isArray(e?.[0])?e[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(t?.cursorIndex)?t.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(t?.multiSelect){let a=r;for(let i of[...n].sort((c,l)=>c-l))s.push(...o(a,i),"Enter"),a=i;s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function Bu(t,e){let n=Number.isInteger(t?.questionIndex)?t.questionIndex:0,o=(Number.isInteger(e)?e:n)-n;if(!o)return[];let s=o>0?"Tab":"BTab";return Array.from({length:Math.abs(o)},()=>s)}function Mn(t){return typeof t=="string"?t.trim():""}var Du=/^\s*─{8,}\s*$/,Wy=/^❯[  ]?(.*)$/;function Na(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
52
- `).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Du.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Du.test(n[c])){o=c;break}if(o<0)return null;let s=n.slice(o+1,r);if(!s.length)return null;let a=s[0].match(Wy);if(!a)return null;let i=[a[1].trim(),...s.slice(1).map(c=>c.trim())].filter(Boolean);return i.length?i.join(" "):null}function Ye(t){return String(t||"").replace(/\s+/g," ").trim()}function ju(t,e){let n=Ye(t),r=Ye(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import{execFile as qy}from"node:child_process";import{promisify as Gy}from"node:util";var Da=Gy(qy),Lu=64*1024*1024;async function mt(t,e,{input:n}={}){let r=Da("git",["-C",t,...e],{maxBuffer:Lu});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function ja(t){if(!t)return!1;try{let{stdout:e}=await mt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Uu(t){let{stdout:e}=await mt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function Ky(t){try{return await mt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Fu(t,e="bostrat: subtask snapshot"){let{stdout:n}=await mt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Da("git",["-C",t,...a],{maxBuffer:Lu,env:o,...i});try{let a=await Ky(t);a&&await s(["read-tree","HEAD"]),await s(["add","-A"]);let{stdout:i}=await s(["write-tree"]),c=i.trim(),l=a?["commit-tree",c,"-p","HEAD","-m",e]:["commit-tree",c,"-m",e],{stdout:u}=await s(l);return u.trim()}finally{try{await Da("rm",["-f",r])}catch{}}}async function Hu(t,e){let{stdout:n}=await mt(t,["status","--porcelain"]);return n.trim()?(await mt(t,["add","-A"]),await mt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function yo(t,e,n,r){return await mt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function La(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await mt(t,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(e.trim())return e.trim()}catch{}for(let[e,n]of[["refs/remotes/origin/main","origin/main"],["refs/remotes/origin/master","origin/master"],["refs/heads/main","main"],["refs/heads/master","master"]])try{return await mt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function Wu(t){await mt(t,["init","-q"]),await mt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Ze(t,e){try{await mt(t,["worktree","remove","--force",e])}catch{try{await mt(t,["worktree","prune"])}catch{}}}function Vy(t){let e=/(\d+) files? changed/.exec(t),n=/(\d+) insertions?\(\+\)/.exec(t),r=/(\d+) deletions?\(-\)/.exec(t);return{files:e?Number(e[1]):0,insertions:n?Number(n[1]):0,deletions:r?Number(r[1]):0}}function Ua(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function qu(t,e,n,r){let{stdout:o}=await mt(t,Ua(["diff","--shortstat",e,n],r));return Vy(o)}async function Gu(t,e,n,r){let{stdout:o}=await mt(t,Ua(["diff","--name-only",e,n],r));return o.split(`
53
- `).map(s=>s.trim()).filter(Boolean)}async function Ku(t,e,n,r){let{stdout:o}=await mt(t,Ua(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Ba(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Ba(t,o,["--3way","--whitespace=nowarn"]),a=await Jy(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Ba(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Ba(t,e,n){try{return await mt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function Jy(t){try{let{stdout:e}=await mt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
54
- `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import zy from"node:fs/promises";import Xy from"node:os";import Vu from"node:path";var bo=t=>new Promise(e=>setTimeout(e,t)),Yy=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,Zy=300,Yu=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 4.8"},{alias:"sonnet",label:"Sonnet 4.6"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],Qy=new Set([...Yu.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),tb=[{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.4",label:"GPT-5.4"},{alias:"gpt-5.4-mini",label:"GPT-5.4 Mini"},{alias:"gpt-5.3-codex",label:"Codex 5.3"},{alias:"gpt-5.2",label:"GPT-5.2"}],Zu=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Qu=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-4-8",label:"Opus 4.8"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}];function td(t){return t==="codex"?tb:t==="cursor"?Qu:Yu}function Ju(t){let e=String(t||"").split(`
55
- `),n=e.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(e[o]))break}return r.length?r:null}function zu(t){let e=String(t||"").split(`
56
- `),n=e.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(e[o]))break}return r.length?r:null}function eb(t){let e=String(t||"").split(`
57
- `);for(let n=e.length-1;n>=0;n--){let r=e[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function Xu(t){let e=String(t||"").split(`
58
- `),n=e.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(e[n]),o=[];for(let s=n+1;s<e.length;s++){let a=e[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function ed(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim();if(t==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!Zu.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=Qu.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!Qy.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function yr(t,e,{timeoutMs:n=Yy}={}){let r=Date.now()+n;for(;;){let o=await pt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await bo(Zy)}}async function nb(t,e){return await yt(t,`/model ${e.model}`,re("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function rb(t,e){await eo(t),await yt(t,"/model",{enter:!1}),await bo(250),await gt(t,["Enter"],{kind:"model-switch"});let n=await yr(t,Ju);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${e.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=eb(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await yt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await yr(t,i=>zu(i)||!Ju(i)&&o(i)&&{done:o(i)});if(!s)throw await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?Zu.get(e.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await gt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await yt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await yr(t,i=>!zu(i)&&o(i));if(!a)throw new Error("codex never confirmed the model change");return{verified:!0,model:a.model,effort:a.effort}}async function ob(){let t=Vu.join(process.env.CURSOR_CONFIG_DIR||Vu.join(Xy.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await zy.readFile(t,"utf8"))?.model||null}catch{return null}}async function sb(t,e){await eo(t),await ro(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await yr(t,Xu),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(e.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await yr(t,Xu,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await gt(t,["Down"],{kind:"model-switch"}),await bo(250)}if(!n||!s||!o(s)){await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await gt(t,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${e.filter}"`:"cursor model picker didn't open")}await gt(t,["Enter"],{kind:"model-switch"}),await bo(800);let a=await ob();return{verified:!!a&&(a.modelId===e.model||a.displayModelId===e.model||r(a.displayName).startsWith(r(e.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function nd(t,e,n){return t==="codex"?rb(e,n):t==="cursor"?sb(e,n):nb(e,n)}import Fa from"node:fs/promises";import Pn from"node:path";import ab from"node:os";import{EventEmitter as ib}from"node:events";import{randomUUID as cb}from"node:crypto";var lb=new Set(["session.ready","turn.user","turn.agent","turn.retracted","tool.called","tool.result","prompt.raised","prompt.answered","context.usage","cost.recorded","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","provider.connected","stall.detected","error"]),ub=new Set(["user","agent","orchestrator","system"]),db=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Pt=new ib;Pt.setMaxListeners(0);var Ha=process.env.BOSTRAT_STATE_DIR||Pn.join(process.env.HOME||ab.tmpdir(),".bostrat");function xo(t){return t?.cwd?Pn.join(t.cwd,".bostrat","events"):Pn.join(Ha,"events")}var rd=(t,e)=>Pn.join(t,`${e}.ndjson`),ko=new Map;async function fb(t,e){if(!ko.has(e)){let o=(await B(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);ko.set(e,o)}let n=ko.get(e)+1;return ko.set(e,n),n}function pb(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function R(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!lb.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!ub.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!db.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Pn.join(Ha,"events"):xo(t),o=e.epoch;if(o==null&&typeof t=="object"&&e.boxId&&Array.isArray(t?.boxes)){let c=t.boxes.find(l=>l.name===e.boxId);o=pb(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await fb(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||cb(),trackId:n,boxId:e.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:e.ts??Date.now(),type:e.type,actor:e.actor??"system",provider:e.provider??null,source:e.source??"orchestrator",payload:e.payload??{},...e.causationId?{causationId:e.causationId}:{},...e.correlationId?{correlationId:e.correlationId}:{}};return await Fa.mkdir(r,{recursive:!0}),await Fa.appendFile(rd(r,n),JSON.stringify(i)+`
59
- `),Pt.emit("event",i),Pt.emit(`track:${n}`,i),i}async function B(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Pn.join(Ha,"events"):xo(t));let o;try{o=await Fa.readFile(rd(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
60
- `))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function od(t,e){let n=`track:${t}`;return Pt.on(n,e),()=>Pt.off(n,e)}var Ot=t=>t||null;function vo(t){let e=t||{},n=mb(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function mb(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{text:t.text,boxRole:t.boxRole,...t.kind?{kind:t.kind}:{},...t.attachments?.length?{attachments:t.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{saidTs:t.saidTs,text:t.text,boxRole:t.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{text:t.prompt,kind:t.kind,boxRole:t.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Ot(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ot(t.toAgent),payload:{box:t.box,fromAgent:t.fromAgent,toAgent:t.toAgent,reason:t.reason,error:t.error,rolledBack:t.rolledBack,handoffStartedAt:t.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Ot(t.toAgent),payload:{task:t.task,subtaskId:t.subtaskId,reason:t.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:t.box||t.from,provider:Ot(t.agent),payload:{...br(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Ot(t.agent||t.toAgent),payload:{kind:"subtask-failed",...br(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{change:"subtask-resumed",...br(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...br(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...br(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{change:"rehydrated"}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Ot(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var So=Promise.resolve(),qa=t=>(So=So.then(t).catch(()=>{}),So),ad=()=>So;function id(t,e){let n=vo(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;qa(()=>R(r,n))}var sd=new Set;async function cd(t){if(!(!t?.id||sd.has(t.id))){sd.add(t.id);try{let e=await B(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=vo(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function br(t){let{type:e,at:n,...r}=t;return r}var Wa=new Map;function ld(t,e,n){!n?.promptId||Wa.get(t.id)===n.promptId||(Wa.set(t.id,n.promptId),qa(()=>R(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Ot(n.provider||e?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function ud(t,e,n,r={}){Wa.delete(t.id),qa(()=>R(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Ot(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as Wb}from"node:crypto";var dd=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,fd=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,pd=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,hb=new RegExp(`\\b(?:${dd}|${fd}|${pd})\\b`,"i"),wb=/\b(?:want\s+me\s+to|would\s+you\s+like(?:\s+me)?(?:\s+to)?|should\s+i|shall\s+i|do\s+you\s+want(?:\s+me)?(?:\s+to)?|happy\s+to|ready\s+for\s+me\s+to|let\s+me\s+know\s+(?:if|when|whether|and)|say\s+the\s+word|give\s+me\s+the\s+(?:word|go[\s-]?ahead)|if\s+you(?:'d|\s+would)?\s+(?:like|want|prefer)|just\s+say\s+(?:so|yes|go))\b/i,gb=/^(?:commit|push|open|create|raise|ready|ok(?:ay)?\s+to|good\s+to)\b/i,yb=90;function bb(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function md(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of bb(n).reverse()){if(!hb.test(r))continue;let o=r.endsWith("?")&&r.length<=yb&&gb.test(r);if(wb.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var hd=["commit","push","pr"],kb=new RegExp(`\\b(?:${dd})\\b`,"i"),xb=new RegExp(`\\b(?:${fd})\\b`,"i"),Sb=new RegExp(`\\b(?:${pd})\\b`,"i"),vb=/\b(?:to|into|onto|on|updat(?:e|es|ing))\s+(?:the|that|this)\s+(?:open\s+|existing\s+|current\s+|same\s+)?(?:draft\s+)?(?:pr|pull\s+request)\b/i;function wd(t){let e=String(t||""),n=[];kb.test(e)&&n.push("commit"),xb.test(e)&&n.push("push");let r=vb.test(e);!r&&Sb.test(e)&&n.push("pr");let o=null;if(r){let s=e.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||e.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function To(t,{branch:e=null,base:n=null,toPr:r=null}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let o=new Set(t),s=e?` on this branch (${e})`:"",a=[];if(o.has("commit")&&a.push(`commit the work${s} with a clear commit message`),o.has("push")||o.has("pr")){let l=r&&!o.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"";a.push(o.has("commit")?`push it to the remote${l}`:`push this branch${s} to the remote${l}`)}o.has("pr")&&a.push(`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let i=[];o.has("commit")||i.push("don't create any new commits \u2014 ship what's already committed"),!o.has("push")&&!o.has("pr")&&i.push("don't push"),o.has("pr")||i.push("don't open a pull request");let c=`Yes \u2014 go ahead: ${a.join(", then ")}.`;return i.length&&(c+=` ${i.join(", and ").replace(/^./,l=>l.toUpperCase())}.`),o.has("pr")&&(c+=" If a PR for this branch is already open, just push the update instead."),c}var Tb=3e4;function gd(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&e-r.lastActivity<=Tb&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function yd(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}import{execFile as Eb}from"node:child_process";import{promisify as Ib}from"node:util";import Ao from"node:fs/promises";import bd from"node:path";import Ab from"node:os";var kd=process.env.BOSTRAT_STATE_DIR||bd.join(process.env.HOME||Ab.tmpdir(),".bostrat"),Ka=bd.join(kd,"pr-links.json"),bt=(t,e)=>`${t}#${Number(e)}`,_o=null,Ga=null,D=Promise.resolve();async function xt(){return _o||(Ga??=Ao.readFile(Ka,"utf8").then(t=>JSON.parse(t)?.links??{}).catch(()=>({})).then(t=>_o=t).finally(()=>Ga=null),Ga)}async function Z(){let t=JSON.stringify({links:_o},null,2),e=`${Ka}.${process.pid}.tmp`;await Ao.mkdir(kd,{recursive:!0}),await Ao.writeFile(e,t),await Ao.rename(e,Ka)}var _b=new Set(["api","observed","reconciled"]);async function Nt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await xt(),c=bt(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:_b.has(a),firstSeen:Date.now()},D=D.then(Z,Z),await D.catch(()=>{}),{link:i[c],created:!0})}async function kr(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||n.announced?null:(n.announced=!0,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Tt(t,e){return!t||e==null?null:(await xt())[bt(t,e)]??null}async function Qe(t,e,n){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function xd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[bt(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function Sd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[bt(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function Eo(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,D=D.then(Z,Z),await D.catch(()=>{}),r)}async function Io(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Ro(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,D=D.then(Z,Z),await D.catch(()=>{}),r)}async function $o(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function vd(t,e,n){if(!t||e==null||!n)return null;let r=(await xt())[bt(t,e)];return r?(r.lastHeadSha=n,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Oo(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Va(t,e,n=!0){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Co(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Td(){return Object.values(await xt()).filter(t=>t.state!=="merged"&&t.state!=="closed")}async function Ja(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await pt(t,{lines:Rt}).catch(()=>"");if(!ur(e,r))return vl(e,r)?{working:!0,prompted:!1}:n;if(Cn(e,r)){let o=await me(t).catch(()=>r);if(Xe({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function Mo(t,e){return(await Ja(t,e)).working}var Ad=Ib(Eb),_d=[4e3,15e3,45e3],Rb=2500,$b=5*6e4;async function za(t,e,{midTurn:n=Mo,pollMs:r=Rb,capMs:o=$b}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}var Ob=6e4,Cb=9e4,Mb=2e3,Pb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,Nb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,Bb=/\s--web\b/,Ed=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Xa(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Id(t){let e=String(t||""),n=e.match(Pb);if(!n)return null;let r=Xa(e,n.index);return Bb.test(r)?null:{repo:r.match(Ed)?.[1]??null}}var Db=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function Rd(t){let e=String(t||""),n=e.match(Nb);if(!n)return null;let r=Xa(e,n.index),o=Xa(e,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){Db.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Ed)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var jb="number,url,title,headRefName,createdAt";async function Lb(t,e){let n=async s=>{let{stdout:a}=await Ad("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",jb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...s}});return JSON.parse(a||"[]")},r=await Gt({slug:t}),o=await n(r);return!o.length&&r.GH_TOKEN&&(o=await n({}).catch(()=>[])),o.filter(s=>Date.parse(s.createdAt)>=e-Ob).sort((s,a)=>Date.parse(a.createdAt)-Date.parse(s.createdAt))}async function Ub(t,e){if(e)return[e];let n=[],r=await Y(t.workspace?.base||t.cwd).catch(()=>null);r&&n.push(r);for(let o of await oe().catch(()=>[])){let s=await Y(o.path).catch(()=>null);s&&!n.includes(s)&&n.push(s)}return n}async function Fb(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await Ub(t,e)){let a;try{a=await Lb(s,n)}catch{continue}for(let i of a){let c=await Tt(s,i.number);if(!(c&&(c.trackId!==t.id||c.announced))){if(r&&i.headRefName===r)return{slug:s,pr:i};o.push({slug:s,pr:i})}}}return o.length?(o.sort((s,a)=>Date.parse(a.pr.createdAt)-Date.parse(s.pr.createdAt)),o[0]):null}var ke=new Set;function $d(t,e,{delays:n=_d,turnEnd:r}={}){let o=Id(e.command);if(!o)return null;let s=!!Rd(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(ke.has(a))return null;ke.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(g=>setTimeout(g,l));let u=await Fb(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:d,pr:f}=u,{link:w,created:m}=await Nt({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(w?.trackId===t.id&&await kr(d,f.number))?null:(await za(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{ke.delete(a)}})();return c.catch(()=>ke.delete(a)),c}var Hb="number,url,title,headRefName,state";function Od(t,e,{delays:n=_d,turnEnd:r,openedWait:o}={}){let s=Rd(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(ke.has(a))return null;ke.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);if(u&&([,c,l]=u),c||=await Y(t.workspace?.base||t.cwd).catch(()=>null),l||=t.workspace?.branch,!c||!l)return null;let d=async(w,m)=>{let g=await Tt(c,w.number);return g&&g.trackId!==t.id?null:(g||await Nt({repo:c,number:w.number,trackId:t.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Oo(c,w.number),m&&((await B(t).catch(()=>[])).some(S=>S.type==="pr.state.changed"&&(S.payload?.change==="auto-armed"||S.payload?.change==="merged")&&S.payload?.number===w.number)||(await za(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(T=>setTimeout(T,w));let m;try{let{stdout:T}=await Ad("gh",["pr","view",String(l),"--repo",c,"--json",Hb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:c})}});m=JSON.parse(T||"null")}catch{continue}if(!m?.number)continue;if(m.state==="CLOSED")return null;if(m.state!=="MERGED"){if(f=m,s.auto)return d(m,!0);continue}let g=await Tt(c,m.number);if(g&&g.trackId!==t.id)return null;g||await Nt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await za(e.box,e.agent,r);let x=await B(t).catch(()=>[]);if(x.some(T=>T.type==="pr.state.changed"&&T.payload?.change==="merged"&&T.payload?.number===m.number))return null;let A=T=>T.some(E=>E.type==="pr.state.changed"&&E.payload?.change==="opened"&&E.payload?.number===m.number),S=0;if(!A(x)){if(Id(e.command)&&await kr(c,m.number)){let T=Date.now();S=T+1,await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:T,change:"opened",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c,selfMerge:!0}})}else if((await Tt(c,m.number))?.announced){let T=o?.capMs??Cb,E=o?.pollMs??Mb,J=Date.now()+T;for(;Date.now()<J&&(await new Promise(M=>setTimeout(M,E)),!A(await B(t).catch(()=>[]))););}}return await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),S),change:"merged",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}),await tn(c,m.number).catch(()=>{}),await Qe(c,m.number,"merged"),m}return f?d(f,!1):null}finally{ke.delete(a)}})();return i.catch(()=>ke.delete(a)),i}function Za(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Cd(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Md(t){return`job|${t.box}|id:${t.id}`}var Ya=new Map,Po=new Map;async function Qa(t){return Ya.has(t.id)?Ya.get(t.id):(Po.has(t.id)||Po.set(t.id,B(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(Za({role:o,sourceId:r.payload.sourceId})),n.keys.add(Za({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(Cd({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(Md({id:r.payload.id,box:r.boxId}));return Ya.set(t.id,n),n}).finally(()=>Po.delete(t.id))),Po.get(t.id))}async function Pd(t,e){try{let n=(await Qa(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Za(r);if(!n.has(o)&&(n.add(o),await R(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch)){let s=md(r.text);if(s){let{offered:a,toPr:i}=wd(s);await R(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:Wb(),ts:r.ts,excerpt:s,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:a||void 0,toPr:i||void 0}})}}}}catch{}}async function Nd(t,e){try{let n=await Qa(t),r=[...e||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Cd(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await R(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),$d(t,o),Od(t,o))}}catch{}}async function Bd(t,e){try{let n=await Qa(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Md(o);n.keys.has(s)||(n.keys.add(s),await R(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var qb=/^Base directory for this skill:\s*(\S+)/,Gb=/^Run the "([^"]+)" workflow\./,Kb=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,Vb=/^<local-command-(?:stdout|caveat)>/i,Jb=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,zb=/^<task-notification>/i,Xb=/<summary>([\s\S]*?)<\/summary>/i,Yb=/<task-id>([\s\S]*?)<\/task-id>/i,Zb=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,Qb=/<status>([\s\S]*?)<\/status>/i,tk=/<result>([\s\S]*?)(?:<\/result>|$)/i;var ti=new Set(["model","clear","compact","login"]);function Dd(t){return ti.has(String(t||"").replace(/^\//,"").toLowerCase())}var ek=/^\/([a-z0-9:_-]+)$/i;function No(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(qb);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(Gb);if(r)return{kind:"skill",name:r[1]};let o=e.match(Kb);if(o){let a=o[1].toLowerCase();return ti.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(ek);if(s&&ti.has(s[1].toLowerCase()))return{kind:"suppress"};if(Vb.test(e))return{kind:"suppress"};if(Jb.test(e))return{kind:"suppress"};if(zb.test(e)){let a=c=>nk(e.match(c)?.[1].trim()||"")||null,i=a(tk);return{kind:"task",summary:a(Xb)||"",taskId:a(Yb),toolUseId:a(Zb),status:a(Qb)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function nk(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function jd(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":""}function Ld(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var at=t=>t.payload?.ts??t.ts;function Bo(t){let e=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,d=new Map;for(let h of t||[]){if(h.type!=="tool.called"||h.source!=="transcript")continue;let p=String(h.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(p))for(let b of p.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let v=b[0].split("/").pop().toLowerCase();d.has(v)||d.set(v,[]),d.get(v).push(h.payload?.ts??h.ts)}}let f=3*6e4,w=20*6e4,m=(h,p)=>!!(h?.name&&d.get(String(h.name).toLowerCase()))?.some(v=>v>=p-f&&v<=p+w);for(let h of t||[])if(h.type==="turn.retracted"){r.add(`${h.boxId}|${h.payload?.saidTs}`);let p=Ye(h.payload?.text);p&&(o.has(h.boxId)||o.set(h.boxId,[]),o.get(h.boxId).push(p))}else if(h.type==="turn.user"&&h.source==="orchestrator"){let p=Ye(h.payload?.text);p&&(s.has(h.boxId)||s.set(h.boxId,new Set),s.get(h.boxId).add(p))}else if(h.type==="job.started"&&h.payload?.id)a.add(h.payload.id),(h.payload.kind||"agent")==="agent"&&h.payload.name&&c.add(h.payload.name);else if(h.type==="turn.user"&&h.payload?.note?.kind==="task"){let p=h.payload.note,b={status:p.status||null,summary:p.summary||null,resultHead:p.resultHead??null,ts:at(h)};p.toolUseId&&i.set(p.toolUseId,b);let v=String(p.summary||"").match(u);v&&l.set(v[1],b)}let g=new Map,x=new Map,A=new Map,S=new Map,T=new Map,E=new Map,J=new Map,M=new Map,I=(h,p)=>{let b=o.get(h);if(!b?.length)return!1;let v=Ye(p),ot=s.get(h);return b.some($=>{if(!v.startsWith($))return!1;let z=Ye(v.slice($.length));return!!z&&!!ot?.has(z)})};for(let h of t||[]){let p=h.payload||{};switch(h.type){case"turn.user":{if(h.source==="hook"||!String(p.text||"").trim()&&!p.images?.length)break;{let b=p.note||No(p.text);if(b){if(b.kind==="task"){if(b.toolUseId&&a.has(b.toolUseId))break;let v=String(b.summary||"").match(u);if(!b.toolUseId&&v&&c.has(v[1]))break}if(b.kind==="command"&&Dd(b.command))break;b.kind!=="suppress"&&e.push({id:h.id,kind:"event",type:Ld(b),name:b.name??null,command:b.command??null,summary:b.summary??null,ts:at(h),box:h.boxId,agent:h.provider});break}}if(r.has(`${h.boxId}|${at(h)}`)||h.source==="transcript"&&I(h.boxId,p.text)||p.kind==="mock-picked"||p.kind==="ci-fix"||p.kind==="reconcile"||p.kind==="pr-go"||p.kind==="push-go"||p.kind==="spin-up-view")break;for(let b of g.values())b.status="stale";g.clear(),e.push({id:h.id,kind:"message",role:"user",text:p.text,ts:at(h),box:h.boxId,agent:h.provider,boxRole:p.boxRole,...h.actor==="orchestrator"?{orchestration:p.kind||!0}:{},...p.sourceId?{sourceId:p.sourceId}:{},...p.attachments?.length?{attachments:p.attachments}:{},...p.images?.length?{images:p.images}:{}});break}case"turn.agent":{J.clear();let b=(p.images||[]).filter(ot=>!m(ot,at(h)));if(!String(p.text||"").trim()&&!b.length)break;if(h.provider==="cursor"){let ot=`${h.boxId}|${p.text}`;if(n.has(ot))break;n.add(ot)}let v=h.provider==="codex"?(p.tools||[]).filter(ot=>!xa.has(ot)&&!wr.has(ot)):p.tools||[];e.push({id:h.id,kind:"message",role:"assistant",text:p.text,ts:at(h),box:h.boxId,agent:h.provider,boxRole:p.boxRole,...v.length?{tools:v}:{},...p.model?{model:p.model}:{},...p.sourceId?{sourceId:p.sourceId}:{},...b.length?{images:b}:{}});break}case"job.started":{if(!p.id)break;let b=at(h),v=i.get(p.id);if(!v&&(p.kind||"agent")==="agent"&&p.name){let N=l.get(p.name);N&&N.ts>=b&&(v=N)}let ot={jobId:p.id,jobKind:p.kind||"agent",name:p.name||null,detail:p.detail||null,status:v?v.status&&v.status!=="completed"?"failed":"done":"running",startedTs:b,finishedTs:v?.ts??null,summary:v?.summary??null,resultHead:v?.resultHead??null},$=M.get(h.boxId);if($&&$.jobs.some(N=>(N.finishedTs??1/0)>=b))$.jobs.push(ot);else{let N={id:h.id,kind:"job-dock",dockId:p.id,ts:b,box:h.boxId,agent:h.provider,jobs:[ot]};M.set(h.boxId,N),e.push(N)}break}case"handoff.done":e.push(xe(h,"lead-handoff"));break;case"subtask.delegated":e.push({...xe(h,"subtask-delegated"),to:h.boxId,toAgent:h.provider});break;case"subtask.merged":e.push(xe(h,"subtask-merged"));break;case"pr.state.changed":if(!p.change)break;if(p.change==="opened"){for(let b of g.values())b.status="stale";g.clear()}e.push(xe(h,`pr-${p.change}`));break;case"pr.offered":{if(!p.offerId)break;for(let v of g.values())v.status="stale";g.clear();let b={id:h.id,kind:"pr-offer",offerId:p.offerId,excerpt:p.excerpt||null,branch:p.branch||null,base:p.base||null,offered:Array.isArray(p.offered)&&p.offered.length?p.offered:null,toPr:p.toPr&&typeof p.toPr=="object"?{number:p.toPr.number??null}:null,status:"open",ts:at(h),box:h.boxId,agent:h.provider};g.set(p.offerId,b),x.set(p.offerId,b),e.push(b);break}case"pr.offer.answered":{let b=x.get(p.offerId);b&&(b.status=p.action||"dismiss",b.actions=Array.isArray(p.actions)?p.actions:null,g.delete(p.offerId));break}case"box.state.changed":{let b=p.change==="model"?"model-change":p.change;if(!b)break;let v=xe(h,b);b==="model-change"&&(v.to=p.model),e.push(v);break}case"view.state.changed":{if(p.change!=="created"||p.restarted)break;e.push({id:h.id,kind:"view-card",viewId:p.viewId,name:p.name,slug:p.slug,port:p.port,origin:p.origin,ts:at(h),box:h.boxId,agent:h.provider});break}case"view.spinup.failed":{e.push({id:h.id,kind:"view-spinup-failed",name:p.name||null,reason:p.reason||"no-view",ts:at(h),box:h.boxId,agent:h.provider});break}case"mock.state.changed":{if(p.change==="picked"){e.push({id:h.id,kind:"mock-picked",mockId:p.mockId,name:p.name,group:p.group??null,version:p.version??null,siblings:p.siblings??[],ts:at(h),box:h.boxId,agent:h.provider});break}if(p.change!=="created")break;e.push({id:h.id,kind:"mock-card",mockId:p.mockId,name:p.name,group:p.group??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"runbook.state.changed":{if(p.change!=="created")break;e.push({id:h.id,kind:"runbook-card",runbookId:p.runbookId,name:p.name,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.linked":{e.push({id:h.id,kind:"ticket-card",provider:p.provider||"linear",key:p.key,url:p.url,title:p.title??null,linkSource:p.linkSource??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.move.offered":{if(!p.offerId)break;for(let v of A.values())v.key===p.key&&(v.status="stale",A.delete(v.offerId));let b={id:h.id,kind:"ticket-offer",offerId:p.offerId,provider:p.provider||"linear",key:p.key,url:p.url??null,phase:p.phase??null,from:p.from??null,to:p.to?.name??null,prNumber:p.prNumber??null,status:"open",ts:at(h),box:h.boxId,agent:h.provider};A.set(p.offerId,b),S.set(p.offerId,b),e.push(b);break}case"ticket.move.answered":{let b=S.get(p.offerId);b&&(b.status=p.action==="moved"?"moved":"dismiss",A.delete(p.offerId));break}case"ticket.state.changed":{for(let b of A.values())b.key===p.key&&b.to===p.to&&(b.status="stale",A.delete(b.offerId));if(p.mode==="offer")break;e.push(xe(h,"ticket-state"));break}case"ticket.commented":{e.push({id:h.id,kind:"ticket-comment",key:p.key,url:p.url??null,excerpt:p.excerpt??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.flow.changed":e.push(xe(h,"ticket-flow"));break;case"ticket.create.offered":{if(!p.proposalId)break;for(let v of T.values())v.status="stale";T.clear();let b={id:h.id,kind:"ticket-create-offer",proposalId:p.proposalId,provider:p.provider||"linear",epic:p.epic??null,issues:Array.isArray(p.issues)?p.issues:[],team:p.team??null,needsTeam:!!p.needsTeam,linkCurrentTrack:!!p.linkCurrentTrack,status:"open",ts:at(h),box:h.boxId,agent:h.provider};T.set(p.proposalId,b),E.set(p.proposalId,b),e.push(b);break}case"ticket.create.answered":{let b=E.get(p.proposalId);b&&(b.status=p.action==="created"?"created":"dismiss",b.createdCount=p.createdCount??null,b.failedCount=p.failedCount??null,T.delete(p.proposalId));break}case"ticket.created":{e.push({id:h.id,kind:"ticket-created",items:Array.isArray(p.items)?p.items:[],failed:Array.isArray(p.failed)?p.failed:[],team:p.team??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"error":{if(!p.kind)break;e.push(xe(h,p.kind));break}case"provider.connected":{e.push({id:h.id,kind:"event",type:"provider-connected",provider:p.provider||h.provider,ts:at(h),at:at(h),box:h.boxId,agent:h.provider});break}case"prompt.answered":{if(p.control||!p.question)break;let b=p.groupId||p.promptId;if(!b)break;let v=J.get(b);v||(v={id:h.id,kind:"prompt",groupId:b,provider:h.provider,box:h.boxId,agent:h.provider,questionTotal:p.questionTotal??1,questions:[],ts:at(h)},J.set(b,v));let ot=p.questionIndex??0,$={index:ot,header:p.header||"",question:p.question,multiSelect:!!p.multiSelect,chosen:Array.isArray(p.chosen)?p.chosen:[]},z=v.questions.findIndex(W=>W.index===ot);z>=0?v.questions[z]=$:v.questions.push($),v.questions.sort((W,_)=>W.index-_.index),v.questionTotal=Math.max(v.questionTotal,p.questionTotal??1),new Set(v.questions.map(W=>W.index)).size>=v.questionTotal&&(e.push(v),J.delete(b));break}default:break}}return ok(e.sort((h,p)=>(h.ts??0)-(p.ts??0)))}var rk=2*6e4;function ok(t){let e=new Map,n=[];for(let r of t){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=e.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=rk){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};e.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else e.delete(r.box),n.push(r)}return n}function ei(t,e=null){let n=[];for(let o of t||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:sk(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(e??Bo(t)).filter(o=>o.kind==="message");return Ql(n,r)}function sk(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==In(t.command)?e:ye(t.command,t.description||"")}function xe(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:at(t),ts:at(t)}}var ni={"claude-opus-4-8":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"claude-sonnet-5":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-sonnet-4-6":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-haiku-4-5":{input:1,output:5,cacheWrite:1.25,cacheRead:.1},"claude-fable-5":{input:10,output:50,cacheWrite:12.5,cacheRead:1},"gpt-5.5":{input:5,output:30,cacheWrite:5,cacheRead:.5},"gpt-5.6-sol":{input:5,output:30,cacheWrite:6.25,cacheRead:.5},"gpt-5.6-terra":{input:2.5,output:15,cacheWrite:3.125,cacheRead:.25},"gpt-5.6-luna":{input:1,output:6,cacheWrite:1.25,cacheRead:.1}},Ud={opus:"claude-opus-4-8",opusplan:"claude-opus-4-8",default:"claude-opus-4-8",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"};function Fd(t){if(!t)return null;let e=String(t).trim();return ni[e]?e:Ud[e]?Ud[e]:Object.keys(ni).find(r=>e.startsWith(r))||null}var Do=t=>Fd(t)!==null;function Kt(t,e={}){let n=ni[Fd(t)];return n?((e.input||0)*n.input+(e.output||0)*n.output+(e.cacheWrite||0)*n.cacheWrite+(e.cacheRead||0)*n.cacheRead)/1e6:0}function ak(t){let e=[],n=null;for(let r of String(t||"").split(`
61
- `)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;e.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return e}var ri=new Map;async function ik(t,e){let n=`${t.id}:${e.name}`;if(!ri.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);ri.set(n,o)}return ri.get(n)}async function Hd(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Ge(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await ik(t,e),o=ak(await vt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await R(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:Kt(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await R(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}import jo from"node:fs";import si from"node:path";import ck from"node:os";var lk=process.env.BOSTRAT_STATE_DIR||si.join(process.env.HOME||ck.tmpdir(),".bostrat"),qd=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,uk="https://cursor.com/api/dashboard/get-filtered-usage-events",Wd=100;async function dk({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(uk,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${e}`},body:JSON.stringify({teamId:0,startDate:String(t),endDate:String(Date.now()),page:o,pageSize:Wd})});if(!s.ok)throw new Error(`cursor usage feed: HTTP ${s.status}`);let a=await s.json(),i=a?.usageEventsDisplay||a?.usageEvents||[];if(r.push(...i),i.length<Wd)break}return r}function fk(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}var pk=5*6e4,Se={at:0,rows:null,error:null},Lo=()=>si.join(lk,"cursor-usage.json");function Gd(){try{return JSON.parse(jo.readFileSync(Lo(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function mk(t){try{jo.mkdirSync(si.dirname(Lo()),{recursive:!0});let e=Lo()+".tmp";jo.writeFileSync(e,JSON.stringify(t)),jo.renameSync(e,Lo())}catch{}}var hk=5e3;async function wk({fetchImpl:t,now:e=Date.now()}={}){if(Se.rows&&e-Se.at<pk)return Se.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Gd(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await dk({sinceMs:o,cookie:n,fetchImpl:t});Se={at:e,rows:s.map(fk).filter(Boolean),error:null}}catch(s){Se={at:e,rows:null,error:String(s?.message||s)}}return Se.rows}var Kd=()=>({enabled:qd(),lastPollAt:Se.at||null,error:Se.error}),oi=new Map;async function gk(t,e){let n=`${t.id}:${e.name}`;if(!oi.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);oi.set(n,o)}return oi.get(n)}async function Vd(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!qd())return;let r=await wk({fetchImpl:n});if(!r||!r.length)return;let o=await cu(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await gk(t,e),a=Gd(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${t.id}:${e.name}:${l.id}`))continue;s.add(l.id),i.add(`${t.id}:${e.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=Kt(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await R(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}mk({sinceMs:c,seen:[...i].slice(-hk)})}catch{}}import Sr from"node:fs/promises";import At from"node:path";import yk from"node:os";import bk from"node:net";import{randomUUID as kk}from"node:crypto";import{execFile as xk}from"node:child_process";import{promisify as Sk}from"node:util";var Jt=Sk(xk),vk=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||yk.tmpdir(),".bostrat"),Zd=At.join(vk,"views.json"),[Jd,zd]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),ve=Number(process.env.BOSTRAT_VIEW_MAX)||3,Tk="bostrat-view-",Nn=t=>`${Tk}${t}`,Xd=new Set(["bostrat","www","api","views","app"]),Uo=t=>Object.assign(new Error(t),{status:400}),Ak=new Set(["npm","pnpm","yarn","bun"]),ci="package.json",_k=/[|;&`]|\$\(/,Ek=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Ik=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Qd=t=>String(t).trim().split(/\s+/).filter(Boolean),Rk=/^[A-Za-z_][A-Za-z0-9_]*=/;function $k(t){for(let e of Qd(t))if(!Rk.test(e))return e;return null}function tf(t){let e=Qd(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function Ok(t,e){let n=tf(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return At.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return At.resolve(t,n[r+1])}return t}var Ck=t=>Sr.stat(t).then(e=>e.isDirectory(),()=>!1),ef=t=>Sr.stat(t).then(e=>e.isFile(),()=>!1),Fo=t=>Sr.realpath(t).catch(()=>At.resolve(t));async function Mk(t,e){if(At.resolve(t)===At.resolve(e))return!0;let[n,r]=await Promise.all([Fo(t),Fo(e)]);return n===r}async function Pk(t){let n=(await Sr.readdir(t,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await ef(At.join(t,o.name,ci))?`./${o.name}`:null))).filter(Boolean)}async function Nk(t,e){let[n,r]=await Promise.all([Fo(t),Fo(e)]),o=At.relative(n,r);if(o&&(o.startsWith("..")||At.isAbsolute(o)))throw Uo(`serve directory escapes the Track workspace: ${e}`)}async function Bk(t,e){if(!await Ck(t))throw Uo(`serve directory does not exist: ${t}`);let n=$k(e);if(!n)return;if(!n.includes("/")&&!await Ds(n,{loginShell:!0,failOpen:!0}))throw Uo(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(_k.test(e)||Ek.has(n)||!Ak.has(n)||!tf(e).includes("run")||Ik.test(e))return;let r=Ok(t,e);if(await ef(At.join(r,ci)))return;let o=await Pk(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw Uo(`no ${ci} in ${r}${s}`)}var Vt=null,ai=null;async function ie(){return Vt||(ai||=(async()=>{try{Vt=JSON.parse(await Sr.readFile(Zd,"utf8"))}catch{Vt={views:[],adoptWatermarks:{}}}finally{ai=null}return Vt.views||=[],Vt.adoptWatermarks||={},Vt})(),ai)}async function zt(){await qt(Zd,Vt)}function ae(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var xr=new Map,di=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,nf=12e3;function fi(t){return xr.get(t)||null}function rf(t,{name:e,box:n}={}){let r=xr.get(t);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:t,name:e||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return xr.set(t,o),o}function li(t,e){let n=xr.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ho(t,{error:e=null}={}){let n=xr.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function of(t){let e=[];for(let n of t?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||e.push({box:n.box,from:n.startedAt,to:n.endedAt});return e}function ui(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function Dk(t,e){let n=ui(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Xd.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Xd.has(s))return s}}function vr(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=bk.connect({port:t,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(e,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function pi(t){if(process.platform!=="darwin"){await Jt("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Jt("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${t}`]))}catch{return}for(let n of e.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}async function jk(t,e){let n=new Set(t.views.map(r=>r.port));if(e){let r=Number(e);if(!Number.isInteger(r)||r<1024||r>65535)throw new Error("invalid port");if(n.has(r))throw new Error(`port ${r} is already owned by a view`);return r}for(let r=Jd;r<=zd;r++)if(!n.has(r)&&!await vr(r,{timeoutMs:250}))return r;throw new Error(`no free view port in ${Jd}-${zd}`)}async function Lk(t){try{let{stdout:e}=await Jt(O,C("list-sessions","-F","#{session_name}"));return e.split(`
62
- `).some(n=>n.trim()===Nn(t))}catch{return!1}}async function Tr(t){try{await Jt(O,C("kill-session","-t",Nn(t)))}catch{}}async function mi(t){if(!await ee())throw new Error("host tmux server not running \u2014 cannot spawn a view server");let e=Nn(t.slug),n=t.serveCwd??t.cwd,r=At.dirname(process.execPath);await Jt(O,C("new-session","-d","-s",e,"-c",n)),await Jt(O,C("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Jt(O,C("send-keys","-t",e,"Enter")),await Jt(O,C("send-keys","-t",e,"-l",t.cmd)),await Jt(O,C("send-keys","-t",e,"Enter"))}async function Yd(t,e=12){try{let{stdout:n}=await Jt(O,C("capture-pane","-t",Nn(t),"-p"));return String(n||"").trimEnd().split(`
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&&eo())try{e.paneTail=(await pt(e.sessionName,{lines:35})).slice(-4e3)}catch{e.paneTail=null}(eo()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed)/.test(String(e.phase||e.step||"")))&&await tw(e)}async function rl({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 ol={DEBUG_DIR:Ms,LOG_FILE:kn};var tt=nw(ew);function ar(t){return new Promise(e=>setTimeout(e,t))}function Ns(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",Rt=process.env.BOSTRAT_TMUX_SOCKET||"/tmp/tmux-1000/default",ow=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Bs=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ds="bostrat-usage-probe",sw=new Set([Ds]);function js(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 Ls(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 tt(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function Us(t){return Ls(String(t||"").trim().split(/\s+/)[0])}function C(...t){return["-S",Rt,...t]}async function pe(){if(!Ps.existsSync(Rt))return!1;try{return await tt(O,C("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function ir(){if(await pe())return!0;try{Ps.mkdirSync(Rt.replace(/\/[^/]+$/,""),{recursive:!0}),await tt(O,C("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await pe()}var aw=/^[A-Za-z0-9_.-]{1,64}$/;function ut(t){return typeof t=="string"&&aw.test(t)}async function vn(){if(!Ps.existsSync(Rt))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 tt(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=>!sw.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function G(t){return(await vn()).find(n=>n.name===t)||null}var iw=/^[A-Za-z0-9 ._-]+$/,cw=64;function lw(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=cw&&iw.test(e)}function Fs(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"")}async function uw(t){try{let{stdout:e}=await tt(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=ow,n={}){if(!lw(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Fs(r);if(!ut(o))throw new Error("resulting tmux session name is invalid");if(!await ir())throw new Error(`host tmux server not running on ${Rt} and auto-start failed \u2014 start one: ${O} -S ${Rt} new-session -d -s keeper "sleep 2147483647"`);if(await uw(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Bs,a=n.agent||Sn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||rw(),l=i?s:js(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 fe(e),a==="codex"&&await yn(e),a==="cursor"&&await bn(e));let w=d&&!f?Qr():null,m=w?["-e",`ANTHROPIC_API_KEY=${w}`]:[];try{await tt(O,C("new-session","-d","-s",o,"-c",e,...m))}catch(g){throw w?Ns(g,w):g}return await Fe(o),await tt(O,C("set-option","-t",o,"@bostrat-session","1")),await tt(O,C("set-option","-t",o,"@bostrat-title",r)),await tt(O,C("set-option","-t",o,"@bostrat-sid",c)),await tt(O,C("set-option","-t",o,"@bostrat-agent",a)),await tt(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 tt(O,C("kill-session","-t",t))}async function sl(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 tt(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 tt(O,n);return String(r||"")}catch{return""}}var dw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,fw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,$t=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,pw=Number(process.env.BOSTRAT_BOX_COLS)||120,mw=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function hw(t){if(!ut(t))return 0;try{let{stdout:e}=await tt(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=pw,rows:n=mw}={}){if(ut(t)&&!await hw(t))try{let r=await il(t);if(r.cols===e&&r.rows===n)return;await tt(O,C("resize-window","-t",t,"-x",String(e),"-y",String(n))),await tt(O,C("set-window-option","-t",t,"window-size","latest")),await ar(80)}catch{}}async function al(t){if(!ut(t))return 0;try{let{stdout:e}=await tt(O,C("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function il(t){if(!ut(t))return{cols:0,rows:0};try{let{stdout:e}=await tt(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 no(t,{cols:e=dw,rows:n=fw}={}){if(ut(t))try{let r=await il(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await tt(O,C("resize-window","-t",t,"-x",String(o),"-y",String(s))),await ar(80))}catch{}}async function me(t){return await no(t),pt(t,{lines:$t})}var ww=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=>ww.has(s));for(let s=0;s<o.length;s++)await tt(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 ro(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 tt(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 oo(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 tt(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 tt(O,C("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await ar(s),await tt(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 gw from"node-pty";async function ll(t,e,n={}){if(!ut(e)){so(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await pe()){so(t,`host tmux server not running on ${Rt}`),t.close(1011,"tmux server unavailable");return}if(!await G(e)){so(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=cl(n.cols,120,ul),o=cl(n.rows,32,dl),s;try{s=gw.spawn(O,["-S",Rt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){so(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Hs(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Hs(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"&&yw(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 ul=20,dl=6;function yw(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=ul&&Number(e)>=dl}function cl(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Hs(t,e){try{t.send(JSON.stringify(e))}catch{}}function so(t,e){Hs(t,{type:"error",message:e})}function fl(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 cr from"node:fs";import bw from"node:fs/promises";import Tn from"node:path";import kw from"node:os";var pl={"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},xw=2e5,Ws=process.env.BOSTRAT_CLAUDE_PROJECTS||Tn.join(kw.homedir(),".claude","projects");function qs(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Gs(t){let e=Tn.join(Ws,qs(t)),n;try{n=cr.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}=cr.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function ee(t,e){if(!e)return null;let n=Tn.join(Ws,qs(t),`${e}.jsonl`);return cr.existsSync(n)?n:null}function ml(t){if(!t)return[];let e=Tn.join(Ws,qs(t)),n;try{n=cr.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=cr.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 bw.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 Sw(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 lr(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=ee(t,e),o=!0,!r)return null}else if(n){if(r=Gs(t),o=!1,!r)return null}else return null;let s;try{s=Sw(await vt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(pl,s.model),i=pl[s.model]||xw;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 vw(t,e){return!t||!e?null:ee(t,e)}function Tw(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 Aw(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 _w(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Ks(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 hl(t,e){if(!t.uuid)return null;let n=Ks(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 Ew=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Vs(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"&&Ew.test(g)&&s.set(m.id,g.split("/").pop())}if(_w(l.content)){let m=hl(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=Tw(l.content),f=u==="assistant"?Aw(l.content):null,w=u==="user"?hl(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 Iw=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function wl(t,e,{turns:n=16}={}){let r=vw(t,e);if(!r)return"";let o=await Vs(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}:** ${Iw(s.text||c,700)} ${i}`.trim()}).join(`
21
+
22
+ `):""}import Kw from"node:fs";import na from"node:fs/promises";import uo from"node:path";import Rw from"node:fs/promises";import{execFile as $w}from"node:child_process";import{promisify as Ow}from"node:util";var Js=Ow($w),Cw=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,gl=new Set,yl=Promise.resolve(),zs=null;function kl(t){zs=typeof t=="function"?t:null}function Mw(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function xl({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||gl.has(r)||(gl.add(r),yl=yl.then(()=>Pw({sessionName:t,from:e,to:r})).catch(()=>{}))}async function Pw({sessionName:t,from:e,to:n}){await L({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Js("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await Rw.realpath(r),s=Mw(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Js("npm",["install","-g","@openai/codex"],{env:a,timeout:Cw});let i=(await Js(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 bl({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 bl({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function bl(t){if(zs)try{await zs(t)}catch{}}var Nw=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Xs=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ur=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Ot={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/}},Bw=/Trust this workspace|\[a\]\s*Trust/,Dw=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function jw(t){let e=String(t||"");if(!Ot.codex.block.test(e))return null;let n=e.match(Dw);return{from:n?.[1]||null,to:n?.[2]||null}}function he(t){return new Promise(e=>setTimeout(e,t))}var Lw=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,ao=new Map;function Sl(t,e){let n=Date.now(),r=Lw();for(let[s,a]of ao)n-a>=r&&ao.delete(s);let o=`${t}::${e}`;return ao.has(o)?!1:(ao.set(o,n),!0)}var Uw=68,Fw=/^\s*[─━]{10,}\s*$/;function Hw(t){let e=0;for(let n of String(t).split(`
23
+ `))Fw.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function Ys(t,e){let n=Ot[t];if(!n?.busy)return"idle";if(Zs(t,e))return"unknown";if(n.up&&!n.up.test(e))return"idle";if(n.busy.test(e))return"busy";let r=Hw(e);return t==="claude"&&r>0&&r<Uw?"unknown":"idle"}var Ww=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/;function Tl(t,e){return t==="claude"&&Ww.test(String(e||""))}function dr(t,e){return Ys(t,e)==="busy"}function io(t,e){return Ys(t,e)!=="idle"}function co(t,e){let n=Ot[t];return n?.up?n.up.test(e):!0}function qw(t,e){let n=Ot[t];return n?Zs(t,e)?!1:n.up.test(e)&&Ys(t,e)==="idle":!0}function Zs(t,e){let n=Ot[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 vl(t,e,n){let r=Date.now()+n,o=0;for(await Fe(t).catch(()=>{});;){let s=await pt(t);if(qw(e,s)){await he(ur);return}if(e==="cursor"&&Bw.test(s)&&!Ot.cursor.up.test(s)&&o<3){if(!Sl(t,"cursor-trust")){await he(Xs);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(ur);continue}if(e==="codex"&&o<3){let a=jw(s);if(a){if(!Sl(t,`codex-update:${a.from}->${a.to}`)){await he(Xs);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"}),xl({sessionName:t,from:a.from,to:a.to}),await he(ur);continue}}if(Date.now()>r){if(Zs(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Ot.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"&&Ot.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:!!Ot[e]?.block?.test(s),paneUp:!!Ot[e]?.up?.test(s),paneBusy:!!Ot[e]?.busy?.test(s)}).catch(()=>{}),await he(ur);return}await he(Xs)}}function Al({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 lo({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Al({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=Nw()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Al({agent:r,agentCmd:o});if(i&&t&&await fe(t),c&&t&&await yn(t),l&&t&&await bn(t),(i||c||l)&&n){await vl(n,a,s);return}let u=Ot[r]?r:Ot[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await vl(n,u,s);return}await he(ur)}var Gw=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function ne(t){return t==="codex"?{enter:!0,submitDelayMs:Gw}:{enter:!0}}var _l=".bostrat/handoff",El="latest.md",Il="Checkpoint: write a work-in-progress handoff",Rl="HANDOFF WRITTEN",$l="Resume from .bostrat/handoff/latest.md",Vw="/clear",Jw=t=>`${Il} 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 ${Rl} and take no further action.`,zw=`${$l}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`;function Xw(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Rl:n.startsWith(Il)||n===Vw||n.startsWith($l):!1}var Yw=6e5;function Ol(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-Yw;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 Cl(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 Ml(t,{windows:e=[]}={}){if(Xw(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 Zw=3500,Qw=3e5,Qs=new Map;function fo(t){return Qs.get(t)||null}async function Pl(t){let e=Qs.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:uo.join(n.cwd,_l,El)};return Qs.set(t,r),tg(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function tg(t,e){t.step="summarize",await Nl(t.name,e),t.step="clear";let n=await G(t.name);await ea(t.name,n,"/clear"),await Dl(Zw),t.step="resume",await ea(t.name,n,zw),t.step="done",t.finishedAt=Date.now()}async function Nl(t,e){let n=uo.join(e,_l),r=uo.join(n,El);await na.mkdir(n,{recursive:!0});let o=await ta(r),s=await G(t);return await L({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await ea(t,s,Jw(r)),await eg(r,o,Qw),await L({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await ta(r)}),await ng(n,r),r}async function Bl(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 Nl(t,e.cwd)}async function ta(t){try{return(await na.stat(t)).mtimeMs}catch{return 0}}async function eg(t,e,n){let r=Date.now()+n;for(;;){if(await ta(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Dl(2e3)}}async function ng(t,e){try{if(!Kw.existsSync(e))return;let n=new Date().toISOString().replace(/[:.]/g,"-");await na.copyFile(e,uo.join(t,`${n}.md`))}catch{}}function Dl(t){return new Promise(e=>setTimeout(e,t))}async function ea(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",...ne(r.agent)})}import P from"node:fs/promises";import U from"node:path";import{randomUUID as ts}from"node:crypto";import{execFile as jx}from"node:child_process";import{promisify as Lx}from"node:util";import ra from"node:fs/promises";import rg from"node:path";var og=0;async function qt(t,e,{mode:n}={}){await ra.mkdir(rg.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++og}.tmp`;await ra.writeFile(r,JSON.stringify(e,null,2)+`
24
+ `,n!=null?{mode:n}:void 0),await ra.rename(r,t)}import{execFile as sg}from"node:child_process";import{promisify as ag}from"node:util";import{randomUUID as ig}from"node:crypto";var He=ag(sg),po=t=>new Promise(e=>setTimeout(e,t)),cg=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,jl=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,lg=250,ug=400,Ll={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 Fl(t){let e=await al(t);if(!e)return!1;try{return await He("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function oa(t,e){let n=Date.now()+e;for(;;){if(await Fl(t))return!0;if(Date.now()>n)return!1;await po(lg)}}async function dg(t,e){let n=Ll[e]||Ll.default;n.escape&&(await gt(t,["Escape"],{kind:"swap-exit"}),await po(150)),n.wipe&&await ro(t,400,{kind:"swap-exit"}),await oo(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=ne(e).submitDelayMs||0;await po(Math.max(r,250)),await gt(t,["Enter"],{kind:"swap-exit"})}async function fg(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 Ul(t){await He(O,C("respawn-pane","-k","-t",t))}async function sa(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)||cg;await L({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Fl(t)&&(await dg(t,o),!await oa(t,a)&&(i=!0,await L({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await Ul(t),!await oa(t,jl))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&xn(n),l=c?Qr():null,u=await fg(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?Ns(x,l):x}if(d&&!i&&(await Ul(t),!await oa(t,jl)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,w=e.resumeSessionId||ig(),m=f?n:js(n,w),g=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await fe(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 po(ug),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 vg from"node:fs";import Wl from"node:fs/promises";import fr from"node:path";import Tg from"node:os";import{fileURLToPath as Ag}from"node:url";import{execFile as _g}from"node:child_process";import{promisify as Eg}from"node:util";import ia from"node:fs/promises";import ge from"node:path";import{execFile as pg}from"node:child_process";import{promisify as mg}from"node:util";var hg=mg(pg),Hl=process.env.HOME||"/home/globo",wg=ge.join(process.env.BOSTRAT_STATE_DIR||ge.join(Hl,".bostrat"),"repos"),gg=[...(process.env.BOSTRAT_REPOS_ROOT||ge.join(Hl,"Documents")).split(","),wg].map(t=>t.trim()).filter(Boolean),yg=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,bg=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function kg(t){try{return(await ia.stat(ge.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function xg(t){let e=i=>ia.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 re(){let t=new Map;async function e(n,r){if(t.has(n))return;let o=await kg(n);if(o==="worktree")return;if(o==="repo"){t.set(n,{path:n,name:ge.basename(n),branch:await xg(n)});return}if(r>=yg)return;let s;try{s=await ia.readdir(n,{withFileTypes:!0})}catch{return}for(let a of s)!a.isDirectory()||a.name.startsWith(".")||bg.has(a.name)||await e(ge.join(n,a.name),r+1)}for(let n of gg)await e(ge.resolve(n),0);return[...t.values()].sort((n,r)=>n.name.localeCompare(r.name))}function Sg(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var aa=new Map;async function Y(t){if(!t)return null;if(aa.has(t))return aa.get(t);let e=null;try{let{stdout:n}=await hg("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=Sg(n)}catch{e=null}return aa.set(t,e),e}var ql=Eg(_g),Ig=process.env.BOSTRAT_STATE_DIR||fr.join(process.env.HOME||Tg.tmpdir(),".bostrat"),Rg=fr.join(Ig,"repos"),Gl=/^[^/\s]+\/[^/\s]+$/,$g=5*6e4,Og=6e4,Cg=10*6e4,Kl=fetch,ca=new Map,We=null;function la(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 mo(t=process.env){return!!(la(t)&&t.BOSTRAT_PLANE_TOKEN)}var Vl=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),An=(t,e)=>Object.assign(new Error(t),{status:e});async function ho(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Gl.test(n))throw An("repo must be owner/name",400);let r=ca.get(n);if(r&&r.expiresAt-Date.now()>$g)return r;if(!mo(e))throw An("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Kl(`${la(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Vl(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 ca.set(n,a),a}async function pr({env:t=process.env,force:e=!1}={}){if(!mo(t))return{configured:!1,repos:[],username:null};if(!e&&We&&Date.now()-We.at<Og)return We.data;try{let n=await Kl(`${la(t)}/v1/github/repos`,{headers:Vl(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 Jl(){ca.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 ho(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function ua({env:t=process.env}={}){return(await pr({env:t})).username??null}var Mg=()=>Ag(new URL("../bin/bostrat-git-credential",import.meta.url)),Pg=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Ng(t){let[e,n]=String(t).split("/");return fr.join(Rg,e,n)}async function da(t,{env:e=process.env}={}){let n=(...r)=>ql("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Mg()}" ${Pg(e)}`),await n("config","credential.useHttpPath","true")}async function fa(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Gl.test(String(t||"")))throw An(`bad repo slug "${t}"`,400);let r=Ng(t);if(vg.existsSync(fr.join(r,".git")))return r;let{token:o}=await ho(t,{env:e});await Wl.mkdir(fr.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await ql("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:Cg,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Wl.rm(r,{recursive:!0,force:!0}).catch(()=>{}),An(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await da(r,{env:e}),r}import my from"node:fs/promises";function Bg(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 mr(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 hr(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},Yl=t=>t.startsWith("-"),zl=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(Yl(o)){e.has(o)&&r++;continue}n.push(o)}return n}var Dg={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"},jg={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 Zl(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 Lg(t){let e=Zl(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 pa(t){if(t.includes("--files")){let s=kt(V(t,zl)[0]);return s&&s!=="."?`List files in ${_n(s)}`:"List files"}let e=V(t,zl),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 ma(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 ha(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 Ug(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 Xl(t){return t.find(e=>/^https?:\/\//.test(e))||null}var Fg={git:t=>{let e=V(t,new Set(["-C","-c"])),n=Dg[e[0]];return n?n(e,t):null},gh:t=>{let e=V(t),n=jg[e[0]];return n?n(e):null},sed:Lg,rg:pa,grep:pa,egrep:pa,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])&&!Yl(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=Xl(t);return e?`Fetch ${En(e,56)}`:"Fetch a URL"},wget:t=>{let e=Xl(t);return e?`Fetch ${En(e,56)}`:"Fetch a URL"},npm:ma,pnpm:ma,yarn:ma,npx:t=>{let e=V(t)[0];return e?`Run ${e}`:null},node:t=>ha(t,"Node"),python:t=>ha(t,"Python"),python3:t=>ha(t,"Python"),make:t=>{let e=V(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:Ug,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}},Hg=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function Wg(t){let e=hr(mr(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(st(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return wa(e[2]);let n=st(e[0]);if(Hg.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=Fg[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function wa(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=Bg(e);for(let i=0;i<r.length-1;i++){let c=hr(mr(r[i])),l=hr(mr(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(st(c[0]))&&st(l[0])==="sed"){let u=Zl(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=Wg(i);c&&o.push({d:c,head:st(hr(mr(i))[0]||""),sub:V(hr(mr(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 ya=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||wa(t)||In(t)}function ga(t,e){if(t){if(Array.isArray(t)){for(let n of t)ga(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&ya.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))ga(n,e)}}}async function Ql(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(ga(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 tu(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 qg=/^Async (?:agent|task) launched/i,Gg=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 eu(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&&qg.test(Gg(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 wr from"node:fs";import Vg from"node:fs/promises";import qe from"node:path";import Jg from"node:os";import zg from"node:crypto";import Xg from"node:zlib";var wo=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Lt=(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 ba=t=>`"${String(t).replace(/"/g,'\\"')}"`;function Kg(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 nu(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:Lt(`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 ${ba(e.glob)}`:"",a=Lt(String(r),40);return{command:Lt(`grep ${ba(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:Lt(`glob ${ba(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Lt(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:Lt(`edit ${be(e.path,n)}`,140),description:`Edit ${Rn(e.path)}`}:null;case"Write":return e?.path?{command:Lt(`write ${be(e.path,n)}`,140),description:`Write ${Rn(e.path)}`}:null;case"Delete":return e?.path?{command:Lt(`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:Lt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=Kg(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Lt(`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:Lt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Yg=process.env.BOSTRAT_CURSOR_CHATS||qe.join(Jg.homedir(),".cursor","chats");function su(t){let e=zg.createHash("md5").update(String(t)).digest("hex");return qe.join(Yg,e)}function au(t){try{let e=JSON.parse(wr.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 Zg(t){let e=su(t),n;try{n=wr.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(!wr.existsSync(a))continue;let i=au(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 go(t,e){if(!t)return null;if(e){let n=qe.join(su(t),e),r=qe.join(n,"store.db");if(wr.existsSync(r))return{id:e,dir:n,store:r,...au(n)||{createdAtMs:null,updatedAtMs:null}}}return Zg(t)}function ru(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=Xg.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function ty(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 ey(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!wo.has(r.toolName))continue;let o=nu(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 ny=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():"",ry=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],ou=process.getBuiltinModule?.("node:sqlite"),$n={turns:[],trailingCommands:[],model:null,requestIds:[]};async function ka(t,e){if(!ou)return $n;let{DatabaseSync:n}=ou,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?ru(r,s.latestRootBlobId):null;if(!i)return{...$n,model:a};let c=[],l=[],u=[],d=null,f=[];for(let w of ty(i)){let m=Qg(ru(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(...ey(m.content,e).map((S,T)=>({...S,id:S.id||`${w}:${T}`}))),u.push(...ry(m.content));let x=ny(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 iu(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(wr.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 cu(t,e,{limit:n=80}={}){let r=go(t,e);if(!r)return[];let{turns:o,model:s}=await ka(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=iu(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 lu(t,e){let n=go(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await ka(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 uu(t,e,{limit:n=80}={}){let r=go(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await ka(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=iu(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 du(t,e){let n=go(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await Vg.stat(n.store)).mtimeMs)}catch{return null}}import fu from"node:fs";import Sa from"node:fs/promises";import xa from"node:path";import oy from"node:os";var sy=process.env.BOSTRAT_CODEX_SESSIONS||xa.join(process.env.CODEX_HOME||xa.join(oy.homedir(),".codex"),"sessions");async function pu(t){try{return await Sa.realpath(t)}catch{return t}}function ay(t=sy){let e=[],n=r=>{let o;try{o=fu.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=xa.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:fu.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function iy(t,e=1048576){let n;try{n=await Sa.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 cy(t){let e=await iy(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=ay();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await pu(t);for(let o of n){let s=await cy(o.file);if(s&&(s===t||await pu(s)===r))return o}return null}var ly=new Set(["output_text","text"]),uy=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&ly.has(e.type)).map(e=>e.text).join(`
32
+ `).trim():"";async function mu(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:Ta,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,va.has(g.name)||c.push(g.name);continue}if(g.type!=="message"||g.role!=="assistant")continue;u=!0;let x=uy(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 gr=new Set(["exec_command","shell","exec"]),va=new Set(["wait","write_stdin"]),Ta="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 dy(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 fy(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 py(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=fy(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function hu(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"&&gr.has(u.name)){let w;try{w=JSON.parse(u.arguments||"{}")}catch{continue}let m=dy(w);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=py(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 wu(t,e){let n=await Ge(t,e);if(!n)return null;try{return Math.round((await Sa.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}},Aa=class extends On{transcript(e){return!e?.cwd||!e?.sessionId?null:ee(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Vs(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?Ql(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?eu(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await my.stat(n)).mtimeMs)}catch{return null}}},_a=class extends On{async readMessages(e,n){return cu(e.cwd,e.sessionId,n)}async readCommands(e,n){return uu(e.cwd,e.sessionId,n)}async updatedAt(e){return du(e.cwd,e.sessionId)}},Ea=class extends On{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return mu(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return hu(e.cwd,this.sessionId(e),n)}async updatedAt(e){return wu(e.cwd,this.sessionId(e))}},hy=new On("unknown"),gu=new Map;function Ia(t){return gu.set(t.agent,t),t}function yr(t){return gu.get(t)||hy}Ia(new Aa("claude",ya));Ia(new _a("cursor",wo));Ia(new Ea("codex",gr));var Ra=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function yu(t,e={}){return(await yr(t.agent).readMessages(t,e)).map(Ra(t))}async function bu(t,e={}){return(await yr(t.agent).readCommands(t,e)).map(Ra(t))}async function ku(t,e={}){return(await yr(t.agent).readJobs(t,e)).map(Ra(t))}var wy=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,xu=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function gy(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
36
+ `);if(!xu.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(wy);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"&&xu.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 yo=/(^|\n)\s*›/,Su=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function yy(t,e=""){let n=String(t);if(!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n)||yo.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(Su);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(Su.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 vu=/Enter this one-time code/,by=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function ky(t,e=""){let n=String(t);if(!vu.test(n)||yo.test(n))return null;let r=n.replace(/\r/g,"").split(`
38
+ `),o=r.findIndex(i=>vu.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(by);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 xy(t,e=""){let n=String(t);return!/open the following link to authenticate/.test(n)||yo.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 Sy(t,e=""){let n=String(t);return!/Paste or type your API key/.test(n)||yo.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 Tu(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=ky(e,n)||xy(e,n)||Sy(e,n)||yy(e,n);if(r)return r}return gy(e,{provider:t,box:n})}function Au(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 Iu=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Ca=/Chat about this/i,Ke=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,_u=/^(type something|chat about this|submit)\b/i,Ve=/^[─—-]{6,}\s*$/,Ma=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function Je(t){let e=t.search(Ma);return(e>=0?t.slice(0,e):t).trim()}function vy(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function Ty(t){let e=String(t).trim();return!!(!e||Ve.test(t)||Ma.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function Ay(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=_y(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function _y(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 Eu(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Ru=/^\s*Switch model\?\s*$/m;function Ey(t,e=""){let n=String(t);if(!Ru.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 $u=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,Iy=/^\s*[─—▔-]{6,}\s*$/;function Ry(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||!$u.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&&!(Iy.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 $y(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 $a=/Resume from summary/i,Oy=/^\s*This session is\b/;function Cy(t,e=""){let n=String(t);if(!$a.test(n))return null;let r=n.replace(/\r/g,"").split(`
42
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if(Oy.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=>$a.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 Pa(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 Ou=/Choose the text style that looks best with your terminal/,Cu=/Let's get started\./;function My(t,e=""){let n=String(t);if(!Ou.test(n)||!Cu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
43
+ `),o=Pa(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 Mu=/Select login method:/;function Py(t,e=""){let n=String(t);if(!Mu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
44
+ `),o=Pa(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 Na=/Paste code here if prompted/,Ny=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function By(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!Ny.test(o)||Na.test(t[r]))break;n+=o}return n}function Dy(t,e=""){let n=String(t);if(!Na.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=By(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 Oa=/OAuth error:/,jy=/Press Enter to retry\./;function Ly(t,e=""){let n=String(t);if(!Oa.test(n)||!jy.test(n)||ze.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
46
+ `)].reverse().find(a=>Oa.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 Pu=/Quick safety check: Is this a project you created or one you trust/;function Uy(t,e=""){let n=String(t);if(!Pu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
47
+ `),o=Pa(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 Nu=/^\s*Press Enter to continue\s*$/;function Fy(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||!Nu.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])||Ma.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 Hy(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Ca.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 Wy(t,e=""){let n=String(t);if(!Iu.test(n)||!Ca.test(n))return null;let r=n.replace(/\r/g,"").split(`
49
+ `),o=Hy(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||Ty(r[p])||_u.test(b))){h.desc=vy(b);break}}let d=l.filter(h=>!_u.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?Eu(r[s]):"")||"The agent is asking a question",A=s>=0?Ay(r[s],x):null,S=A?.currentTabLabel||(s>=0?Eu(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"?Iu.test(n)&&Ca.test(n)||Ru.test(n)||$a.test(n)||$u.test(n.trimEnd().split(`
50
+ `).pop()||"")?!0:ze.test(n)?!1:Ou.test(n)&&Cu.test(n)||Mu.test(n)||Na.test(n)||Oa.test(n)||Pu.test(n)||Nu.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"?$y(e,t.name)||Wy(e,t.name)||Ey(e,t.name)||Cy(e,t.name)||Ly(e,t.name)||Dy(e,t.name)||Py(e,t.name)||My(e,t.name)||Uy(e,t.name)||Fy(e,t.name)||Ry(e,t.name):t.agent==="cursor"||t.agent==="codex"?Tu(t.agent,e,t.name):null}async function Ba(t){if(!t)return null;let e=await me(t.name).catch(()=>"");return Xe(t,e)}function Bu(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 Du(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 ju=/^\s*─{8,}\s*$/,qy=/^❯[  ]?(.*)$/;function Da(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(ju.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(ju.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(qy);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 Lu(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 Gy}from"node:child_process";import{promisify as Ky}from"node:util";var La=Ky(Gy),Uu=64*1024*1024;async function mt(t,e,{input:n}={}){let r=La("git",["-C",t,...e],{maxBuffer:Uu});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Ua(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 Fu(t){let{stdout:e}=await mt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function Vy(t){try{return await mt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Hu(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)=>La("git",["-C",t,...a],{maxBuffer:Uu,env:o,...i});try{let a=await Vy(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 La("rm",["-f",r])}catch{}}}async function Wu(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 bo(t,e,n,r){return await mt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Fa(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 qu(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 Jy(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 Ha(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Gu(t,e,n,r){let{stdout:o}=await mt(t,Ha(["diff","--shortstat",e,n],r));return Jy(o)}async function Ku(t,e,n,r){let{stdout:o}=await mt(t,Ha(["diff","--name-only",e,n],r));return o.split(`
53
+ `).map(s=>s.trim()).filter(Boolean)}async function Vu(t,e,n,r){let{stdout:o}=await mt(t,Ha(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await ja(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await ja(t,o,["--3way","--whitespace=nowarn"]),a=await zy(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await ja(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function ja(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 zy(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 Xy from"node:fs/promises";import Yy from"node:os";import Ju from"node:path";var ko=t=>new Promise(e=>setTimeout(e,t)),Zy=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,Qy=300,Zu=[{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"}],tb=new Set([...Zu.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),eb=[{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"}],Qu=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),td=[{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 ed(t){return t==="codex"?eb:t==="cursor"?td:Zu}function zu(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 Xu(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 nb(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 Yu(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 nd(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&&!Qu.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=td.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!tb.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function br(t,e,{timeoutMs:n=Zy}={}){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 ko(Qy)}}async function rb(t,e){return await yt(t,`/model ${e.model}`,ne("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function ob(t,e){await no(t),await yt(t,"/model",{enter:!1}),await ko(250),await gt(t,["Enter"],{kind:"model-switch"});let n=await br(t,zu);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=nb(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 br(t,i=>Xu(i)||!zu(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?Qu.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 br(t,i=>!Xu(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 sb(){let t=Ju.join(process.env.CURSOR_CONFIG_DIR||Ju.join(Yy.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await Xy.readFile(t,"utf8"))?.model||null}catch{return null}}async function ab(t,e){await no(t),await oo(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await br(t,Yu),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 br(t,Yu,{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 ko(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 ko(800);let a=await sb();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 rd(t,e,n){return t==="codex"?ob(e,n):t==="cursor"?ab(e,n):rb(e,n)}import Wa from"node:fs/promises";import Pn from"node:path";import ib from"node:os";import{EventEmitter as cb}from"node:events";import{randomUUID as lb}from"node:crypto";var ub=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"]),db=new Set(["user","agent","orchestrator","system"]),fb=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Nt=new cb;Nt.setMaxListeners(0);var qa=process.env.BOSTRAT_STATE_DIR||Pn.join(process.env.HOME||ib.tmpdir(),".bostrat");function So(t){return t?.cwd?Pn.join(t.cwd,".bostrat","events"):Pn.join(qa,"events")}var od=(t,e)=>Pn.join(t,`${e}.ndjson`),xo=new Map;async function pb(t,e){if(!xo.has(e)){let o=(await B(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);xo.set(e,o)}let n=xo.get(e)+1;return xo.set(e,n),n}function mb(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||!ub.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!db.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!fb.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Pn.join(qa,"events"):So(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=mb(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await pb(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||lb(),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 Wa.mkdir(r,{recursive:!0}),await Wa.appendFile(od(r,n),JSON.stringify(i)+`
59
+ `),Nt.emit("event",i),Nt.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(qa,"events"):So(t));let o;try{o=await Wa.readFile(od(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 sd(t,e){let n=`track:${t}`;return Nt.on(n,e),()=>Nt.off(n,e)}var Ct=t=>t||null;function To(t){let e=t||{},n=hb(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function hb(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Ct(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:Ct(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:Ct(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:Ct(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:Ct(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:Ct(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:Ct(t.agent),payload:{...kr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Ct(t.agent||t.toAgent),payload:{kind:"subtask-failed",...kr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{change:"subtask-resumed",...kr(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",...kr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...kr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{change:"rehydrated"}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var vo=Promise.resolve(),Ka=t=>(vo=vo.then(t).catch(()=>{}),vo),id=()=>vo;function cd(t,e){let n=To(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Ka(()=>R(r,n))}var ad=new Set;async function ld(t){if(!(!t?.id||ad.has(t.id))){ad.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=To(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function kr(t){let{type:e,at:n,...r}=t;return r}var Ga=new Map;function ud(t,e,n){!n?.promptId||Ga.get(t.id)===n.promptId||(Ga.set(t.id,n.promptId),Ka(()=>R(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Ct(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 dd(t,e,n,r={}){Ga.delete(t.id),Ka(()=>R(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Ct(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as qb}from"node:crypto";var 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|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,pd=/(?<!\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,md=/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,wb=new RegExp(`\\b(?:${fd}|${pd}|${md})\\b`,"i"),gb=/\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,yb=/^(?:commit|push|open|create|raise|ready|ok(?:ay)?\s+to|good\s+to)\b/i,bb=90;function kb(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function hd(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of kb(n).reverse()){if(!wb.test(r))continue;let o=r.endsWith("?")&&r.length<=bb&&yb.test(r);if(gb.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var wd=["commit","push","pr"],xb=new RegExp(`\\b(?:${fd})\\b`,"i"),Sb=new RegExp(`\\b(?:${pd})\\b`,"i"),vb=new RegExp(`\\b(?:${md})\\b`,"i"),Tb=/\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 gd(t){let e=String(t||""),n=[];xb.test(e)&&n.push("commit"),Sb.test(e)&&n.push("push");let r=Tb.test(e);!r&&vb.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 Ao(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 Ab=3e4;function yd(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<=Ab&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function bd(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 Ib}from"node:child_process";import{promisify as Rb}from"node:util";import _o from"node:fs/promises";import kd from"node:path";import _b from"node:os";var xd=process.env.BOSTRAT_STATE_DIR||kd.join(process.env.HOME||_b.tmpdir(),".bostrat"),Ja=kd.join(xd,"pr-links.json"),bt=(t,e)=>`${t}#${Number(e)}`,Eo=null,Va=null,D=Promise.resolve();async function xt(){return Eo||(Va??=_o.readFile(Ja,"utf8").then(t=>JSON.parse(t)?.links??{}).catch(()=>({})).then(t=>Eo=t).finally(()=>Va=null),Va)}async function Z(){let t=JSON.stringify({links:Eo},null,2),e=`${Ja}.${process.pid}.tmp`;await _o.mkdir(xd,{recursive:!0}),await _o.writeFile(e,t),await _o.rename(e,Ja)}var Eb=new Set(["api","observed","reconciled"]);async function Bt({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:Eb.has(a),firstSeen:Date.now()},D=D.then(Z,Z),await D.catch(()=>{}),{link:i[c],created:!0})}async function xr(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 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.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function vd(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 Io(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 Ro(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 $o(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 Oo(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 Td(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 Co(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 za(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 Mo(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 Ad(){return Object.values(await xt()).filter(t=>t.state!=="merged"&&t.state!=="closed")}async function Xa(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await pt(t,{lines:$t}).catch(()=>"");if(!dr(e,r))return Tl(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 Po(t,e){return(await Xa(t,e)).working}var _d=Rb(Ib),Ed=[4e3,15e3,45e3],$b=2500,Ob=5*6e4;async function Ya(t,e,{midTurn:n=Po,pollMs:r=$b,capMs:o=Ob}={}){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 Cb=6e4,Mb=9e4,Pb=2e3,Nb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,Bb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,Db=/\s--web\b/,Id=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Za(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Rd(t){let e=String(t||""),n=e.match(Nb);if(!n)return null;let r=Za(e,n.index);return Db.test(r)?null:{repo:r.match(Id)?.[1]??null}}var jb=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function $d(t){let e=String(t||""),n=e.match(Bb);if(!n)return null;let r=Za(e,n.index),o=Za(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("-")){jb.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Id)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var Lb="number,url,title,headRefName,createdAt";async function Ub(t,e){let n=async s=>{let{stdout:a}=await _d("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",Lb],{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-Cb).sort((s,a)=>Date.parse(a.createdAt)-Date.parse(s.createdAt))}async function Fb(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 re().catch(()=>[])){let s=await Y(o.path).catch(()=>null);s&&!n.includes(s)&&n.push(s)}return n}async function Hb(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await Fb(t,e)){let a;try{a=await Ub(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 Od(t,e,{delays:n=Ed,turnEnd:r}={}){let o=Rd(e.command);if(!o)return null;let s=!!$d(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 Hb(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:d,pr:f}=u,{link:w,created:m}=await Bt({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 xr(d,f.number))?null:(await Ya(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 Wb="number,url,title,headRefName,state";function Cd(t,e,{delays:n=Ed,turnEnd:r,openedWait:o}={}){let s=$d(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 Bt({repo:c,number:w.number,trackId:t.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Co(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 Ya(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 _d("gh",["pr","view",String(l),"--repo",c,"--json",Wb],{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 Bt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Ya(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(Rd(e.command)&&await xr(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??Mb,E=o?.pollMs??Pb,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 ti(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Md(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Pd(t){return`job|${t.box}|id:${t.id}`}var Qa=new Map,No=new Map;async function ei(t){return Qa.has(t.id)?Qa.get(t.id):(No.has(t.id)||No.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(ti({role:o,sourceId:r.payload.sourceId})),n.keys.add(ti({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(Md({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(Pd({id:r.payload.id,box:r.boxId}));return Qa.set(t.id,n),n}).finally(()=>No.delete(t.id))),No.get(t.id))}async function Nd(t,e){try{let n=(await ei(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=ti(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=hd(r.text);if(s){let{offered:a,toPr:i}=gd(s);await R(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:qb(),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 Bd(t,e){try{let n=await ei(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=Md(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}}),Od(t,o),Cd(t,o))}}catch{}}async function Dd(t,e){try{let n=await ei(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Pd(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 Gb=/^Base directory for this skill:\s*(\S+)/,Kb=/^Run the "([^"]+)" workflow\./,Vb=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,Jb=/^<local-command-(?:stdout|caveat)>/i,zb=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,Xb=/^<task-notification>/i,Yb=/<summary>([\s\S]*?)<\/summary>/i,Zb=/<task-id>([\s\S]*?)<\/task-id>/i,Qb=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,tk=/<status>([\s\S]*?)<\/status>/i,ek=/<result>([\s\S]*?)(?:<\/result>|$)/i;var ni=new Set(["model","clear","compact","login"]);function jd(t){return ni.has(String(t||"").replace(/^\//,"").toLowerCase())}var nk=/^\/([a-z0-9:_-]+)$/i;function Bo(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(Gb);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(Kb);if(r)return{kind:"skill",name:r[1]};let o=e.match(Vb);if(o){let a=o[1].toLowerCase();return ni.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(nk);if(s&&ni.has(s[1].toLowerCase()))return{kind:"suppress"};if(Jb.test(e))return{kind:"suppress"};if(zb.test(e))return{kind:"suppress"};if(Xb.test(e)){let a=c=>rk(e.match(c)?.[1].trim()||"")||null,i=a(ek);return{kind:"task",summary:a(Yb)||"",taskId:a(Zb),toolUseId:a(Qb),status:a(tk)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function rk(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function Ld(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 Ud(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var at=t=>t.payload?.ts??t.ts;function Do(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||Bo(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"&&jd(b.command))break;b.kind!=="suppress"&&e.push({id:h.id,kind:"event",type:Ud(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=>!va.has(ot)&&!gr.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 sk(e.sort((h,p)=>(h.ts??0)-(p.ts??0)))}var ok=2*6e4;function sk(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<=ok){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 ri(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:ak(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??Do(t)).filter(o=>o.kind==="message");return tu(n,r)}function ak(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 oi={"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}},Fd={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 Hd(t){if(!t)return null;let e=String(t).trim();return oi[e]?e:Fd[e]?Fd[e]:Object.keys(oi).find(r=>e.startsWith(r))||null}var jo=t=>Hd(t)!==null;function Kt(t,e={}){let n=oi[Hd(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 ik(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 si=new Map;async function ck(t,e){let n=`${t.id}:${e.name}`;if(!si.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);si.set(n,o)}return si.get(n)}async function Wd(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 ck(t,e),o=ik(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 Lo from"node:fs";import ii from"node:path";import lk from"node:os";var uk=process.env.BOSTRAT_STATE_DIR||ii.join(process.env.HOME||lk.tmpdir(),".bostrat"),Gd=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,dk="https://cursor.com/api/dashboard/get-filtered-usage-events",qd=100;async function fk({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(dk,{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:qd})});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<qd)break}return r}function pk(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 mk=5*6e4,Se={at:0,rows:null,error:null},Uo=()=>ii.join(uk,"cursor-usage.json");function Kd(){try{return JSON.parse(Lo.readFileSync(Uo(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function hk(t){try{Lo.mkdirSync(ii.dirname(Uo()),{recursive:!0});let e=Uo()+".tmp";Lo.writeFileSync(e,JSON.stringify(t)),Lo.renameSync(e,Uo())}catch{}}var wk=5e3;async function gk({fetchImpl:t,now:e=Date.now()}={}){if(Se.rows&&e-Se.at<mk)return Se.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Kd(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await fk({sinceMs:o,cookie:n,fetchImpl:t});Se={at:e,rows:s.map(pk).filter(Boolean),error:null}}catch(s){Se={at:e,rows:null,error:String(s?.message||s)}}return Se.rows}var Vd=()=>({enabled:Gd(),lastPollAt:Se.at||null,error:Se.error}),ai=new Map;async function yk(t,e){let n=`${t.id}:${e.name}`;if(!ai.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);ai.set(n,o)}return ai.get(n)}async function Jd(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Gd())return;let r=await gk({fetchImpl:n});if(!r||!r.length)return;let o=await lu(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await yk(t,e),a=Kd(),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}})}hk({sinceMs:c,seen:[...i].slice(-wk)})}catch{}}import vr from"node:fs/promises";import At from"node:path";import bk from"node:os";import kk from"node:net";import{randomUUID as xk}from"node:crypto";import{execFile as Sk}from"node:child_process";import{promisify as vk}from"node:util";var Jt=vk(Sk),Tk=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||bk.tmpdir(),".bostrat"),Qd=At.join(Tk,"views.json"),[zd,Xd]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),ve=Number(process.env.BOSTRAT_VIEW_MAX)||3,Ak="bostrat-view-",Nn=t=>`${Ak}${t}`,Yd=new Set(["bostrat","www","api","views","app"]),Fo=t=>Object.assign(new Error(t),{status:400}),_k=new Set(["npm","pnpm","yarn","bun"]),ui="package.json",Ek=/[|;&`]|\$\(/,Ik=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Rk=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,tf=t=>String(t).trim().split(/\s+/).filter(Boolean),$k=/^[A-Za-z_][A-Za-z0-9_]*=/;function Ok(t){for(let e of tf(t))if(!$k.test(e))return e;return null}function ef(t){let e=tf(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function Ck(t,e){let n=ef(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 Mk=t=>vr.stat(t).then(e=>e.isDirectory(),()=>!1),nf=t=>vr.stat(t).then(e=>e.isFile(),()=>!1),Ho=t=>vr.realpath(t).catch(()=>At.resolve(t));async function Pk(t,e){if(At.resolve(t)===At.resolve(e))return!0;let[n,r]=await Promise.all([Ho(t),Ho(e)]);return n===r}async function Nk(t){let n=(await vr.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 nf(At.join(t,o.name,ui))?`./${o.name}`:null))).filter(Boolean)}async function Bk(t,e){let[n,r]=await Promise.all([Ho(t),Ho(e)]),o=At.relative(n,r);if(o&&(o.startsWith("..")||At.isAbsolute(o)))throw Fo(`serve directory escapes the Track workspace: ${e}`)}async function Dk(t,e){if(!await Mk(t))throw Fo(`serve directory does not exist: ${t}`);let n=Ok(e);if(!n)return;if(!n.includes("/")&&!await Ls(n,{loginShell:!0,failOpen:!0}))throw Fo(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(Ek.test(e)||Ik.has(n)||!_k.has(n)||!ef(e).includes("run")||Rk.test(e))return;let r=Ck(t,e);if(await nf(At.join(r,ui)))return;let o=await Nk(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw Fo(`no ${ui} in ${r}${s}`)}var Vt=null,ci=null;async function ae(){return Vt||(ci||=(async()=>{try{Vt=JSON.parse(await vr.readFile(Qd,"utf8"))}catch{Vt={views:[],adoptWatermarks:{}}}finally{ci=null}return Vt.views||=[],Vt.adoptWatermarks||={},Vt})(),ci)}async function zt(){await qt(Qd,Vt)}function se(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 Sr=new Map,pi=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,rf=12e3;function mi(t){return Sr.get(t)||null}function of(t,{name:e,box:n}={}){let r=Sr.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 Sr.set(t,o),o}function di(t,e){let n=Sr.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Wo(t,{error:e=null}={}){let n=Sr.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function sf(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 fi(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function jk(t,e){let n=fi(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Yd.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Yd.has(s))return s}}function Tr(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=kk.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 hi(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 Lk(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=zd;r<=Xd;r++)if(!n.has(r)&&!await Tr(r,{timeoutMs:250}))return r;throw new Error(`no free view port in ${zd}-${Xd}`)}async function Uk(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 Ar(t){try{await Jt(O,C("kill-session","-t",Nn(t)))}catch{}}async function wi(t){if(!await ir())throw new Error("host tmux server not running and auto-start failed \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 Zd(t,e=12){try{let{stdout:n}=await Jt(O,C("capture-pane","-t",Nn(t),"-p"));return String(n||"").trimEnd().split(`
63
63
  `).filter(Boolean).slice(-e).join(`
64
- `)}catch{return""}}var se=new Map,Uk=12e4;async function Fk(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await Lk(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Yd(t.slug)||"server exited before listening"),se.delete(t.id),await ae(t,t.state,t.error?{error:t.error}:{}),!0}if(await vr(t.port))return se.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await ae(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>Uk?(t.state="error",t.error=await Yd(t.slug)||"never started listening",await ae(t,"error",{error:t.error}),!0):!1;let o=(se.get(t.id)||0)+1;return se.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var ii=!1;async function Wo(){if(!ii){ii=!0;try{let t=await ie(),e=!1;for(let n of t.views)await Fk(n)&&(e=!0);e&&await zt()}finally{ii=!1}}}async function sf({trackId:t}={}){await Wo();let e=await ie();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function af(t){let n=(await ie()).views.find(r=>r.id===t);return n?{...n}:null}function hi(t){return Vt&&Vt.views.find(e=>e.slug===t)||null}async function wi({trackId:t,name:e,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=At.resolve(r,o??"."),d=i==="detected"?null:async()=>{await Nk(r,u),await Bk(u,n)},f=await ie();if(i!=="detected"){let g=ui(e),x=String(n).trim(),A=f.views.find(S=>S.trackId===t&&(S.slug===g||ui(S.name)===g||S.cmd===x&&At.resolve(S.serveCwd??S.cwd)===u));if(A)return Hk(f,A,{cmd:x,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(g=>g.state==="live"||g.state==="starting").length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);let m={id:kk().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:Dk(f,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await jk(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await mi(m),f.views.push(m),await zt(),await ae(m,"created",{origin:i}),li(t,m.id),i==="detected"&&await vr(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await zt(),await ae(m,"live")),{...m}}async function Hk(t,e,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(e.box=r),o&&(e.branch=o);let i=e.cmd!==n,c=s!==void 0&&!await Mk(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await vr(e.port))return await zt(),li(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(d=>d.id!==e.id&&(d.state==="live"||d.state==="starting")).length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);return e.origin==="detected"?(await pi(e.port),e.origin="agent"):await Tr(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),se.delete(e.id),await mi(e),await zt(),await ae(e,"created",{restarted:!0,reused:!0}),li(e.trackId,e.id),{...e,reused:!0}}async function cf(t){let n=(await ie()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await pi(n.port):await Tr(n.slug),n.state!=="stopped"&&(n.state="stopped",se.delete(n.id),await zt(),await ae(n,"stopped")),{...n}}async function lf(t){let e=await ie(),n=e.views.find(o=>o.id===t);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(e.views.filter(o=>o.id!==t&&(o.state==="live"||o.state==="starting")).length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);return await Tr(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),se.delete(n.id),await mi(n),await zt(),await ae(n,"created",{restarted:!0}),{...n}}async function uf(t){let e=await ie(),n=e.views.findIndex(o=>o.id===t);if(n===-1)throw new Error("view not found");let r=e.views[n];return r.origin==="detected"?await pi(r.port):await Tr(r.slug),e.views.splice(n,1),se.delete(r.id),await zt(),await ae(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function df(t){let e=await ie(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Tr(r.slug),se.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await zt()),n.length}var Wk=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function ff(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await ie(),r=n.adoptWatermarks[t.id]??null,o=e.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=e.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[t.id]!==s&&(n.adoptWatermarks[t.id]=s,await zt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(Wk)){let u=Number(l[1]||80);u>=1024&&u<=65535&&a.add(u)}if(!a.size)return[];let i=[];for(let c of a)if(c!==Number(process.env.PORT||3362)&&!n.views.some(l=>l.port===c)&&await vr(c))try{i.push(await wi({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}import{execFile as qk}from"node:child_process";import{promisify as Gk}from"node:util";var Ko=Gk(qk),Kk={timeout:3e4,maxBuffer:8*1024*1024};async function Lt(t,e,n={}){let r=await Gt({cwd:t}),{stdout:o}=await Ko("gh",e,{cwd:t,...Kk,...n,env:{...process.env,...r,...n.env||{}}});return String(o||"")}var qo=null;async function bi(){if(qo&&Date.now()-qo.at<6e4)return qo.data;let t;try{let e=await Ko("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await fr();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return qo={at:Date.now(),data:t},t}async function Vk(t){try{let{stdout:e}=await Ko("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function mf(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r;return t.__typename==="StatusContext"?r=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?r="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?r="pass":r="fail",{name:e,state:r,url:n}}function gi(t){let e=(t||[]).map(mf);return{total:e.length,passed:e.filter(n=>n.state==="pass").length,failed:e.filter(n=>n.state==="fail").length,pending:e.filter(n=>n.state==="pending").length,failing:e.filter(n=>n.state==="fail").map(({name:n,url:r})=>({name:n,url:r}))}}var ki="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",hf=ki.replace("statusCheckRollup,","");function xi(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function wf(t,e){if(!e)return null;let n=await Y(t);if(!n)return null;try{let r=await Lt(t,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(e)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function gf(t){return(t||[]).map(e=>({name:e.name,detailsUrl:e.html_url||null,status:String(e.status||"").toUpperCase(),conclusion:e.conclusion?String(e.conclusion).toUpperCase():null}))}async function yf(t,e){let n=await wf(t,e);return n===null?{...gi([]),unavailable:!0}:gi(gf(n))}function Go(t,e=gi(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}var Jk=9e4,Bn=new Map;function zk(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Bn.delete(s);continue}r.add(s),Bn.has(s)||Bn.set(s,n),o.mergeabilityPending=n-Bn.get(s)<Jk}for(let o of Bn.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Bn.delete(o);return e}async function _r(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Lt(n.path,["pr","view",String(r),"--json",ki]);return Go(JSON.parse(o))}catch(o){if(!xi(o))throw o;let s=await Lt(n.path,["pr","view",String(r),"--json",hf]),a=JSON.parse(s);return Go(a,await yf(n.path,a.headRefOid))}}var pf=(t,e)=>e.number-t.number;async function Ar(t){try{let e=await Lt(t,["pr","list","--json",ki,"--limit","50"]);return JSON.parse(e||"[]").map(n=>Go(n)).sort(pf)}catch(e){if(!xi(e))throw e;let n=await Lt(t,["pr","list","--json",hf,"--limit","50"]);return(await Promise.all(JSON.parse(n||"[]").map(async r=>Go(r,await yf(t,r.headRefOid))))).sort(pf)}}var Xk=2e4,Yk=1500,Dn=null;function en(){Dn=null}async function Zk(t,e){if(!t)return e;for(let n of e){let r=await Tt(t,n.number).catch(()=>null);if(!r)continue;r.autoMerge&&(n.autoMergeArmed=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Eo(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ro(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function bf(){let t=await bi();if(!t.authenticated)return{auth:t,repos:[]};let e=Dn?.pending?Yk:Xk;if(Dn&&Date.now()-Dn.at<e)return{auth:t,repos:Dn.data};let n=[];for(let a of await oe())await Vk(a.path)&&n.push(a);let r=Date.now(),o=await Promise.all(n.map(async a=>{try{let i=zk(a.path,await Ar(a.path),r);return await Zk(await Y(a.path).catch(()=>null),i),{name:a.name,path:a.path,prs:i}}catch(i){return{name:a.name,path:a.path,prs:[],error:ce(i)}}})),s=o.some(a=>a.prs.some(i=>i.mergeabilityPending||i.checks.pending>0||i.autoMergeArmed));return Dn={at:Date.now(),data:o,pending:s},{auth:t,repos:o}}async function Te(t){let e=(await oe()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function Si(t){if(!t)return null;for(let e of await oe())if(await Y(e.path).catch(()=>null)===t)return e.path;return null}var Vo=new Set(["squash","merge","rebase"]);async function Er(t,e,{method:n="squash"}={}){let r=await Te(t);if(!Vo.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(e);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await Gt({cwd:r.path})).GH_TOKEN){let a=await ca().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Lt(r.path,s)}catch(a){throw new Error(ce(a))}finally{en()}return{ok:!0,number:o}}async function kf(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Lt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(ce(o))}finally{en()}return{ok:!0,number:r}}var Qk=2500,tx=9e4,ex=t=>new Promise(e=>setTimeout(e,t));function xf(t){return t.green&&!t.isDraft}function nx(t){return t.isDraft?"draft":t.mergeable==="CONFLICTING"?`conflicts with ${t.baseRefName}`:t.checks.failed>0?"checks failing":t.checks.pending>0?"checks running":t.mergeable==="UNKNOWN"||t.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":t.mergeStateStatus==="BEHIND"?`behind ${t.baseRefName}`:t.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${t.mergeStateStatus})`}var yi=new Map;function Sf(){let t={};for(let[e,n]of yi)t[e]={...n};return t}async function vf(t,{method:e="merge",onMerged:n=null}={}){let r=await Te(t);if(!Vo.has(e))throw new Error(`invalid merge method: ${e}`);if(yi.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Ar(r.path)).filter(xf).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:e,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return yi.set(r.path,s),ox(s,o,{listPrs:()=>Ar(r.path),merge:a=>Er(r.path,a,{method:e}),sleep:ex,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function rx(t,e){let n=Date.now()+(e.settleMaxMs??tx),r=null;for(;;){let o=null;try{if(o=await e.listPrs(),r=null,!o.some(a=>t.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await e.sleep(Qk)}}async function ox(t,e,n){try{let r=e;for(let o=0;o<e.length;o++){let s=e[o];if(t.current=s.number,o>0)try{r=await rx(e.slice(o).map(i=>i.number),n)}catch(i){for(let c of e.slice(o))t.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${ce(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){t.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!xf(a)){t.skipped.push({number:a.number,title:a.title,reason:nx(a)});continue}try{await n.merge(a.number),t.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){t.failed.push({number:a.number,title:a.title,reason:ce(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),en()}}async function Tf(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Ko("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${ce(l)}`)}let o=n||"";if((await Gt({cwd:t})).GH_TOKEN){let l=await ca().catch(()=>null);l&&(o=`${o?`${o}
64
+ `)}catch{return""}}var oe=new Map,Fk=12e4;async function Hk(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await Uk(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Zd(t.slug)||"server exited before listening"),oe.delete(t.id),await se(t,t.state,t.error?{error:t.error}:{}),!0}if(await Tr(t.port))return oe.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await se(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>Fk?(t.state="error",t.error=await Zd(t.slug)||"never started listening",await se(t,"error",{error:t.error}),!0):!1;let o=(oe.get(t.id)||0)+1;return oe.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var li=!1;async function qo(){if(!li){li=!0;try{let t=await ae(),e=!1;for(let n of t.views)await Hk(n)&&(e=!0);e&&await zt()}finally{li=!1}}}async function af({trackId:t}={}){await qo();let e=await ae();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function cf(t){let n=(await ae()).views.find(r=>r.id===t);return n?{...n}:null}function gi(t){return Vt&&Vt.views.find(e=>e.slug===t)||null}async function yi({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 Bk(r,u),await Dk(u,n)},f=await ae();if(i!=="detected"){let g=fi(e),x=String(n).trim(),A=f.views.find(S=>S.trackId===t&&(S.slug===g||fi(S.name)===g||S.cmd===x&&At.resolve(S.serveCwd??S.cwd)===u));if(A)return Wk(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:xk().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:jk(f,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await Lk(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await wi(m),f.views.push(m),await zt(),await se(m,"created",{origin:i}),di(t,m.id),i==="detected"&&await Tr(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await zt(),await se(m,"live")),{...m}}async function Wk(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 Pk(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Tr(e.port))return await zt(),di(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 hi(e.port),e.origin="agent"):await Ar(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),oe.delete(e.id),await wi(e),await zt(),await se(e,"created",{restarted:!0,reused:!0}),di(e.trackId,e.id),{...e,reused:!0}}async function lf(t){let n=(await ae()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await hi(n.port):await Ar(n.slug),n.state!=="stopped"&&(n.state="stopped",oe.delete(n.id),await zt(),await se(n,"stopped")),{...n}}async function uf(t){let e=await ae(),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 Ar(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),oe.delete(n.id),await wi(n),await zt(),await se(n,"created",{restarted:!0}),{...n}}async function df(t){let e=await ae(),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 hi(r.port):await Ar(r.slug),e.views.splice(n,1),oe.delete(r.id),await zt(),await se(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function ff(t){let e=await ae(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Ar(r.slug),oe.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await zt()),n.length}var qk=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function pf(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await ae(),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(qk)){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 Tr(c))try{i.push(await yi({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 Gk}from"node:child_process";import{promisify as Kk}from"node:util";var Vo=Kk(Gk),Vk={timeout:3e4,maxBuffer:8*1024*1024};async function Ut(t,e,n={}){let r=await Gt({cwd:t}),{stdout:o}=await Vo("gh",e,{cwd:t,...Vk,...n,env:{...process.env,...r,...n.env||{}}});return String(o||"")}var Go=null;async function xi(){if(Go&&Date.now()-Go.at<6e4)return Go.data;let t;try{let e=await Vo("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 pr();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Go={at:Date.now(),data:t},t}async function Jk(t){try{let{stdout:e}=await Vo("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function hf(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 bi(t){let e=(t||[]).map(hf);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 Si="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",wf=Si.replace("statusCheckRollup,","");function vi(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function gf(t,e){if(!e)return null;let n=await Y(t);if(!n)return null;try{let r=await Ut(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 yf(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 bf(t,e){let n=await gf(t,e);return n===null?{...bi([]),unavailable:!0}:bi(yf(n))}function Ko(t,e=bi(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 zk=9e4,Bn=new Map;function Xk(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)<zk}for(let o of Bn.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Bn.delete(o);return e}async function Er(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 Ut(n.path,["pr","view",String(r),"--json",Si]);return Ko(JSON.parse(o))}catch(o){if(!vi(o))throw o;let s=await Ut(n.path,["pr","view",String(r),"--json",wf]),a=JSON.parse(s);return Ko(a,await bf(n.path,a.headRefOid))}}var mf=(t,e)=>e.number-t.number;async function _r(t){try{let e=await Ut(t,["pr","list","--json",Si,"--limit","50"]);return JSON.parse(e||"[]").map(n=>Ko(n)).sort(mf)}catch(e){if(!vi(e))throw e;let n=await Ut(t,["pr","list","--json",wf,"--limit","50"]);return(await Promise.all(JSON.parse(n||"[]").map(async r=>Ko(r,await bf(t,r.headRefOid))))).sort(mf)}}var Yk=2e4,Zk=1500,Dn=null;function en(){Dn=null}async function Qk(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 Io(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 kf(){let t=await xi();if(!t.authenticated)return{auth:t,repos:[]};let e=Dn?.pending?Zk:Yk;if(Dn&&Date.now()-Dn.at<e)return{auth:t,repos:Dn.data};let n=[];for(let a of await re())await Jk(a.path)&&n.push(a);let r=Date.now(),o=await Promise.all(n.map(async a=>{try{let i=Xk(a.path,await _r(a.path),r);return await Qk(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:ie(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 re()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function Ti(t){if(!t)return null;for(let e of await re())if(await Y(e.path).catch(()=>null)===t)return e.path;return null}var Jo=new Set(["squash","merge","rebase"]);async function Ir(t,e,{method:n="squash"}={}){let r=await Te(t);if(!Jo.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 ua().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ut(r.path,s)}catch(a){throw new Error(ie(a))}finally{en()}return{ok:!0,number:o}}async function xf(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ut(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(ie(o))}finally{en()}return{ok:!0,number:r}}var tx=2500,ex=9e4,nx=t=>new Promise(e=>setTimeout(e,t));function Sf(t){return t.green&&!t.isDraft}function rx(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 ki=new Map;function vf(){let t={};for(let[e,n]of ki)t[e]={...n};return t}async function Tf(t,{method:e="merge",onMerged:n=null}={}){let r=await Te(t);if(!Jo.has(e))throw new Error(`invalid merge method: ${e}`);if(ki.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await _r(r.path)).filter(Sf).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 ki.set(r.path,s),sx(s,o,{listPrs:()=>_r(r.path),merge:a=>Ir(r.path,a,{method:e}),sleep:nx,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function ox(t,e){let n=Date.now()+(e.settleMaxMs??ex),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(tx)}}async function sx(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 ox(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: ${ie(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(!Sf(a)){t.skipped.push({number:a.number,title:a.title,reason:rx(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:ie(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),en()}}async function Af(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Vo("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${ie(l)}`)}let o=n||"";if((await Gt({cwd:t})).GH_TOKEN){let l=await ua().catch(()=>null);l&&(o=`${o?`${o}
65
65
 
66
- `:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",e.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await Lt(t,s)}catch(l){throw new Error(ce(l))}finally{en()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function Af(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Lt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(ce(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!xi(i))throw i;let c=await Lt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await wf(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(ce(i));o=gf(l).map(mf).filter(u=>u.state==="fail").map(u=>({name:u.name,link:u.url}))}let s=new Set,a=[];for(let i of o){let c=sx(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Lt(n.path,["run","view",c,"--log-failed"]).then(u=>ax(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function sx(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function ax(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
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 Ut(t,s)}catch(l){throw new Error(ie(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 _f(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 Ut(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(ie(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!vi(i))throw i;let c=await Ut(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await gf(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(ie(i));o=yf(l).map(hf).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=ax(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Ut(n.path,["run","view",c,"--log-failed"]).then(u=>ix(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function ax(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function ix(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
67
67
  `);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
68
68
  `+o.slice(-e).join(`
69
69
  `)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
70
- `+r.slice(-n)),r}function ce(t){return String(t?.stderr||t?.message||t||"").trim().split(`
71
- `).find(n=>n.trim())||"gh failed"}var ix=6e3,Jo=new Map;function vi(t){let e=null;for(let n of t||[]){let r=n.payload?.ts??n.ts;n.type==="pr.offer.answered"?e=n.payload?.action==="pr"?{ts:r,offerId:n.payload?.offerId??null,box:n.boxId??null}:null:n.type==="pr.state.changed"&&n.payload?.change==="opened"&&e!=null&&r>=e.ts&&(e=null)}return e}async function _f(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await B(t)}catch{return}if(vi(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Mo(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Jo.get(t.id)||0)<ix)return;Jo.set(t.id,o);let s;try{s=await Ar(t.cwd)}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await Y(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Nt({repo:i,number:a.number,trackId:t.id,url:a.url,headRef:e,title:a.title,source:"reconciled"});if(!l&&!(c?.trackId===t.id&&await kr(i,a.number))){Jo.delete(t.id);return}}await R(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e}}).catch(()=>{}),Jo.delete(t.id)}import ht from"node:fs/promises";import _t from"node:path";import Ir from"node:os";import{randomUUID as cx}from"node:crypto";import{execFile as lx}from"node:child_process";import{promisify as ux}from"node:util";import{chromium as dx}from"playwright-core";var If=ux(lx),Rf=process.env.BOSTRAT_STATE_DIR||_t.join(process.env.HOME||Ir.tmpdir(),".bostrat"),$f=_t.join(Rf,"mocks.json"),fx=process.env.BOSTRAT_MOCK_ENGINE||"chromium",px=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",mx=process.env.BOSTRAT_FIREFOX_BIN||"firefox",hx=1300,jn=2,wx=32,Of=1500,Ln=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,gx=700*1024*1024;async function yx(){if(process.platform!=="darwin")return Ir.freemem();try{let{stdout:t}=await If("vm_stat",[],{timeout:3e3}),e=Number(/page size of (\d+) bytes/.exec(t)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(t)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*e;return r>0?r:Ir.freemem()}catch{return Ir.freemem()}}var Ut=class extends Error{constructor(e,n=""){super(n?`${e}
70
+ `+r.slice(-n)),r}function ie(t){return String(t?.stderr||t?.message||t||"").trim().split(`
71
+ `).find(n=>n.trim())||"gh failed"}var cx=6e3,zo=new Map;function Ai(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 Ef(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await B(t)}catch{return}if(Ai(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Po(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(zo.get(t.id)||0)<cx)return;zo.set(t.id,o);let s;try{s=await _r(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 Bt({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 xr(i,a.number))){zo.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(()=>{}),zo.delete(t.id)}import ht from"node:fs/promises";import _t from"node:path";import Rr from"node:os";import{randomUUID as lx}from"node:crypto";import{execFile as ux}from"node:child_process";import{promisify as dx}from"node:util";import{chromium as fx}from"playwright-core";var Rf=dx(ux),$f=process.env.BOSTRAT_STATE_DIR||_t.join(process.env.HOME||Rr.tmpdir(),".bostrat"),Of=_t.join($f,"mocks.json"),px=process.env.BOSTRAT_MOCK_ENGINE||"chromium",mx=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",hx=process.env.BOSTRAT_FIREFOX_BIN||"firefox",wx=1300,jn=2,gx=32,Cf=1500,Ln=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,yx=700*1024*1024;async function bx(){if(process.platform!=="darwin")return Rr.freemem();try{let{stdout:t}=await Rf("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:Rr.freemem()}catch{return Rr.freemem()}}var Ft=class extends Error{constructor(e,n=""){super(n?`${e}
72
72
  ${n.trim().split(`
73
73
  `).slice(-6).join(`
74
- `)}`:e),this.name="MockRenderError"}},nn=null,Ti=null;async function le(){return nn||(Ti||=(async()=>{try{nn=JSON.parse(await ht.readFile($f,"utf8"))}catch{nn={mocks:[]}}finally{Ti=null}return nn.mocks||=[],nn})(),Ti)}async function Xt(){await qt($f,nn)}function Ft(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var zo=t=>_t.join(t,".bostrat","mocks");function Bt(t,e){let n=zo(t.cwd);return{html:_t.join(n,`${t.id}.v${e}.html`),png:_t.join(n,`${t.id}.v${e}.png`)}}function rn(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function _i(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var bx=Buffer.from([137,80,78,71,13,10,26,10]);function Cf(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(bx)}async function kx(t){let e=Buffer.alloc(24),n=await ht.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function xx({imgW:t,imgH:e,bbox:n,pad:r,align:o=1}){if(!n||!(t>0)||!(e>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=t*.9&&a>=e*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function Sx(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,g)=>{r.onload=m,r.onerror=()=>g(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,w=-1;for(let m=0;m<s;m++)for(let g=m*o*4,x=0;x<o;x++,g+=4)Math.abs(c[g]-l[0])+Math.abs(c[g+1]-l[1])+Math.abs(c[g+2]-l[2])>24&&(x<u&&(u=x),x>f&&(f=x),m<d&&(d=m),w=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:w}}},e.toString("base64"))}async function vx(t,e,{bg:n,crop:r}){let o=await t.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function Tx({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await dx.launch({executablePath:px,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ut("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:jn})).newPage();a.setDefaultTimeout(Ln),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Ln}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ln});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await Sx(a,i),u=l&&xx({...l,pad:wx*jn,align:jn});u&&(i=await vx(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Of)throw new Ut(`capture produced a ${i.length}-byte image`);let c=_i(i);if(!c)throw new Ut("output is not a PNG");return await ht.writeFile(e,i),{width:Math.round(c.width/jn),height:Math.round(c.height/jn),dpr:jn,renderMs:Date.now()-r}}catch(s){throw s instanceof Ut?s:new Ut(/timeout/i.test(s?.message||"")?`render timed out after ${Ln}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function Ef({htmlPath:t,outPath:e,width:n}){let r=await ht.mkdtemp(_t.join(Ir.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await If(mx,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ln,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Ut(c?.killed?`render timed out after ${Ln}ms`:"firefox exited non-zero",s)}let a=await ht.stat(e).catch(()=>null);if(!a||a.size<Of)throw new Ut(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=_i(await kx(e));if(!i)throw new Ut("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await ht.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function Ax(t){if(fx==="firefox")return Ef(t);try{return await Tx(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return Ef(t);throw e}}var _x=Ax;var Ai=Promise.resolve();function Mf(t){return Ai=Ai.then(t,t),Ai}async function Pf(t,e){let n=await le(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await Xt(),await Ft(r,"rendering",{version:e});try{let o=await yx();if(o<gx)throw new Ut(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Bt(r,e),a=await _x({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await ht.rm(s.png,{force:!0}).catch(()=>{});return}Nf(r,{v:e,source:"html",htmlRel:_t.relative(r.cwd,s.html),pngRel:_t.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Xt(),await Ft(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr})}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await Xt(),await Ft(r,"error",{version:e,error:r.error})}}}function Nf(t,e){t.versions=(t.versions||[]).filter(r=>r.v!==e.v).concat(e),t.versions.sort((r,o)=>r.v-o.v);let n=t.versions[t.versions.length-1];t.readyVersion=n.v,t.height=n.height,t.dpr=n.dpr,t.renderMs=n.renderMs??null,n.v===t.version&&(t.state="ready",t.error=null)}function Ex({trackId:t,name:e,cwd:n,width:r}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?hx:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Bf({trackId:t,group:e}={}){let n=await le(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function Ae(t){let n=(await le()).mocks.find(r=>r.id===t);return n?{...n}:null}async function Df({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=Ex({trackId:t,name:e,cwd:o,width:s}),d=typeof n=="string"&&n.trim();if(!d&&!r)throw new Error("html (string) or image (png) required");if(r&&!Cf(r))throw new Error("image must be a PNG");let f=await le(),w={id:cx().slice(0,8),trackId:t,box:i||null,agent:c||null,name:e.trim(),prompt:l?String(l).slice(0,500):null,group:a?String(a).trim():null,cwd:o,width:u,height:null,dpr:null,state:"queued",source:d?"html":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await ht.mkdir(zo(o),{recursive:!0}),f.mocks.push(w),d?(await ht.writeFile(Bt(w,1).html,n),await Xt(),await Ft(w,"created"),Mf(()=>Pf(w.id,1))):(await Lf(w,1,r),await Xt(),await Ft(w,"created"),await Ft(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr})),{...w}}async function jf(t,{html:e,image:n,width:r}={}){let s=(await le()).mocks.find(c=>c.id===t);if(!s)throw new Error("mock not found");let a=typeof e=="string"&&e.trim();if(!a&&!n)throw new Error("html (string) or image (png) required");if(n&&!Cf(n))throw new Error("image must be a PNG");if(r!=null){let c=Number(r);if(!Number.isInteger(c)||c<320||c>4e3)throw new Error("invalid width (320-4000)");s.width=c}let i=s.version+1;return s.version=i,s.updatedAt=Date.now(),s.source=a?"html":"image",await ht.mkdir(zo(s.cwd),{recursive:!0}),a?(s.state="queued",await ht.writeFile(Bt(s,i).html,e),await Xt(),await Ft(s,"updated",{version:i}),Mf(()=>Pf(s.id,i))):(await Lf(s,i,n),await Xt(),await Ft(s,"updated",{version:i}),await Ft(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr})),{...s}}async function Lf(t,e,n){let r=_i(n);if(!r)throw new Error("image must be a PNG");let o=Bt(t,e);await ht.writeFile(o.png,n),Nf(t,{v:e,source:"image",htmlRel:null,pngRel:_t.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Uf(t){let e=await le(),n=e.mocks.find(o=>o.id===t);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Ft(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Ff(t){let e=await le(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Bt(r,o);await ht.rm(s.html,{force:!0}).catch(()=>{}),await ht.rm(s.png,{force:!0}).catch(()=>{})}return await Xt(),await Ft(r,"deleted"),{ok:!0,id:t,name:r.name}}async function Hf(t){let n=(await le()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=_t.join(Rf,"mock-archive",t.id),o=zo(t.cwd),s=_t.join(r,".bostrat","mocks");await ht.mkdir(s,{recursive:!0});let a=await ht.readdir(o).catch(()=>[]);for(let i of a)await ht.copyFile(_t.join(o,i),_t.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel]){if(!l)continue;if(!(await ht.stat(_t.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=t.name||t.id}return await Xt(),n.length}async function Wf(t){let e=await le(),n=e.mocks.filter(r=>r.trackId===t);return n.length&&(e.mocks=e.mocks.filter(r=>r.trackId!==t),await Xt()),n.length}import sn from"node:fs/promises";import _e from"node:path";import Ix from"node:os";import{randomUUID as Rx}from"node:crypto";var Fn=process.env.BOSTRAT_STATE_DIR||_e.join(process.env.HOME||Ix.tmpdir(),".bostrat"),Gf=_e.join(Fn,"runbooks.json"),Ii=_e.join(Fn,"runbooks"),qf=256*1024,$x=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];function Ox(t){return $x.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var on=null,Ei=null;async function Ee(){return on||(Ei||=(async()=>{try{on=JSON.parse(await sn.readFile(Gf,"utf8"))}catch{on={runbooks:[]}}finally{Ei=null}return on.runbooks||=[],on})(),Ei)}async function Hn(){await qt(Gf,on)}function Wn(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function Un(t,e){return _e.join(Ii,`${t.id}.v${e}.md`)}function Kf(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Vf(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>qf)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${qf/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ox(t);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return e}function Jf(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function zf({trackId:t,status:e}={}){let n=await Ee(),r=t?n.runbooks.filter(o=>o.trackId===t):n.runbooks;return e&&(r=r.filter(o=>o.status===e)),r.map(o=>({...o}))}async function Ri(t){let n=(await Ee()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Xf({trackId:t,trackName:e,name:n,md:r,cwd:o,box:s,agent:a}){if(!t)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=Vf(r),c=await Ee(),l={id:Rx().slice(0,8),trackId:t,trackName:e||t,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await sn.mkdir(Ii,{recursive:!0}),await sn.writeFile(Un(l,1),r),l.versions.push({v:1,mdRel:_e.relative(Fn,Un(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Hn(),await Wn(l,"created",{version:1}),{...l}}async function Yf(t,{md:e,note:n}={}){let o=(await Ee()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");Jf(o,"update");let s=Vf(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await sn.mkdir(Ii,{recursive:!0}),await sn.writeFile(Un(o,a),e),o.versions.push({v:a,mdRel:_e.relative(Fn,Un(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Hn(),await Wn(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),{...o}}async function Zf(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Ee()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return Jf(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Hn(),await Wn(r,e==="done"?"done":"reopened"),{...r})}async function Qf(t){let e=await Ee(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await sn.rm(_e.join(Fn,o.mdRel),{force:!0}).catch(()=>{});return await Hn(),r.trackDeletedAt||await Wn(r,"deleted"),{ok:!0,id:t,name:r.name}}async function tp(t){let n=(await Ee()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),await Wn(r,"orphaned"),r.cwd=null;return n.length&&await Hn(),n.length}async function ep(t){let e=await Ee(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Wn(r,"deleted");for(let o of r.versions||[])await sn.rm(_e.join(Fn,o.mdRel),{force:!0}).catch(()=>{})}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Hn()),n.length}import np from"node:fs/promises";import Xo from"node:path";import{randomUUID as Cx}from"node:crypto";var op=".bostrat/uploads",$i=t=>Xo.join(t,op),rp=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,Mx=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Px=new Set(["pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","rtf","txt","md","markdown","rst","csv","tsv","json","jsonl","yaml","yml","xml","html","htm","log","ini","toml","conf","env","js","jsx","ts","tsx","py","rb","go","rs","java","kt","c","h","cpp","cc","cs","php","swift","sh","bash","zsh","sql","css","scss"]);function Oi(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return Mx.has(e)?"image":Px.has(e)?"doc":null}function Nx(t){let r=Xo.basename(String(t||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function sp(t,{filename:e,buffer:n}){if(!t)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>rp)throw new Error(`file too large \u2014 max ${Math.floor(rp/(1024*1024))} MB`);let r=Nx(e),o=Xo.extname(r).slice(1),s=Oi(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Cx().slice(0,8),i=$i(t);await np.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await np.writeFile(Xo.join(i,c),n),{id:a,name:r,rel:`${op}/${c}`,kind:s,size:n.length}}function ap(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var Bx=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Ci(t){return t.role==="user"&&Bx.test((t.text||"").trim())}function qn(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var Lx=jx(Dx),Ie=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),pp=U.join(Ie,"tracks.json"),Ux=U.join(Ie,"events.jsonl"),Li=U.join(Ie,"workspaces"),Fx=process.env.BOSTRAT_DEFAULT_REPO||null,ts={claude:{envVar:"BOSTRAT_AGENT_CMD",base:"claude",skipFlag:"--dangerously-skip-permissions"},cursor:{envVar:"BOSTRAT_CURSOR_CMD",base:"cursor-agent",skipFlag:"--force"},codex:{envVar:"BOSTRAT_CODEX_CMD",base:"codex",skipFlag:"--dangerously-bypass-approvals-and-sandbox"}},Fi=Object.keys(ts);function Hx(t,{skipPermissions:e=!0}={}){let n=ts[t];if(!n)throw new Error(`unknown agent "${t}"`);let r=process.env[n.envVar];return r||(e&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function mp(t,e,{skipPermissions:n=!0}={}){let r=ts[t];if(!r)throw new Error(`unknown agent "${t}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(t){case"claude":return`${r.base} --resume ${e}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function es(t){let e=ts[t];if(e)return process.env[e.envVar]||e.base}async function ns(t,e=!0){let n=Hx(t,{skipPermissions:e});if(!await js(n)){let r=n.split(/\s+/)[0];throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). Its CLI must be installed and on PATH before you can hand off / delegate to it.`)}return n}var rs=".bostrat/handoff/latest.md";function Wx(t,e=rs){return`${t.trim()}
74
+ `)}`:e),this.name="MockRenderError"}},nn=null,_i=null;async function ce(){return nn||(_i||=(async()=>{try{nn=JSON.parse(await ht.readFile(Of,"utf8"))}catch{nn={mocks:[]}}finally{_i=null}return nn.mocks||=[],nn})(),_i)}async function Xt(){await qt(Of,nn)}function Ht(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 Xo=t=>_t.join(t,".bostrat","mocks");function Dt(t,e){let n=Xo(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 Ii(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var kx=Buffer.from([137,80,78,71,13,10,26,10]);function Mf(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(kx)}async function xx(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 Sx({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 vx(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 Tx(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 Ax({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await fx.launch({executablePath:mx,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ft("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 vx(a,i),u=l&&Sx({...l,pad:gx*jn,align:jn});u&&(i=await Tx(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Cf)throw new Ft(`capture produced a ${i.length}-byte image`);let c=Ii(i);if(!c)throw new Ft("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 Ft?s:new Ft(/timeout/i.test(s?.message||"")?`render timed out after ${Ln}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function If({htmlPath:t,outPath:e,width:n}){let r=await ht.mkdtemp(_t.join(Rr.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Rf(hx,["--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 Ft(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<Cf)throw new Ft(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Ii(await xx(e));if(!i)throw new Ft("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 _x(t){if(px==="firefox")return If(t);try{return await Ax(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return If(t);throw e}}var Ex=_x;var Ei=Promise.resolve();function Pf(t){return Ei=Ei.then(t,t),Ei}async function Nf(t,e){let n=await ce(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await Xt(),await Ht(r,"rendering",{version:e});try{let o=await bx();if(o<yx)throw new Ft(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Dt(r,e),a=await Ex({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}Bf(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 Ht(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 Ht(r,"error",{version:e,error:r.error})}}}function Bf(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 Ix({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?wx:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Df({trackId:t,group:e}={}){let n=await ce(),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 ce()).mocks.find(r=>r.id===t);return n?{...n}:null}async function jf({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=Ix({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&&!Mf(r))throw new Error("image must be a PNG");let f=await ce(),w={id:lx().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(Xo(o),{recursive:!0}),f.mocks.push(w),d?(await ht.writeFile(Dt(w,1).html,n),await Xt(),await Ht(w,"created"),Pf(()=>Nf(w.id,1))):(await Uf(w,1,r),await Xt(),await Ht(w,"created"),await Ht(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr})),{...w}}async function Lf(t,{html:e,image:n,width:r}={}){let s=(await ce()).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&&!Mf(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(Xo(s.cwd),{recursive:!0}),a?(s.state="queued",await ht.writeFile(Dt(s,i).html,e),await Xt(),await Ht(s,"updated",{version:i}),Pf(()=>Nf(s.id,i))):(await Uf(s,i,n),await Xt(),await Ht(s,"updated",{version:i}),await Ht(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr})),{...s}}async function Uf(t,e,n){let r=Ii(n);if(!r)throw new Error("image must be a PNG");let o=Dt(t,e);await ht.writeFile(o.png,n),Bf(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 Ff(t){let e=await ce(),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 Ht(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Hf(t){let e=await ce(),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=Dt(r,o);await ht.rm(s.html,{force:!0}).catch(()=>{}),await ht.rm(s.png,{force:!0}).catch(()=>{})}return await Xt(),await Ht(r,"deleted"),{ok:!0,id:t,name:r.name}}async function Wf(t){let n=(await ce()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=_t.join($f,"mock-archive",t.id),o=Xo(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 qf(t){let e=await ce(),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 Rx from"node:os";import{randomUUID as $x}from"node:crypto";var Fn=process.env.BOSTRAT_STATE_DIR||_e.join(process.env.HOME||Rx.tmpdir(),".bostrat"),Kf=_e.join(Fn,"runbooks.json"),$i=_e.join(Fn,"runbooks"),Gf=256*1024,Ox=[{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 Cx(t){return Ox.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var on=null,Ri=null;async function Ee(){return on||(Ri||=(async()=>{try{on=JSON.parse(await sn.readFile(Kf,"utf8"))}catch{on={runbooks:[]}}finally{Ri=null}return on.runbooks||=[],on})(),Ri)}async function Hn(){await qt(Kf,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($i,`${t.id}.v${e}.md`)}function Vf(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 Jf(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>Gf)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${Gf/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Cx(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 zf(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Xf({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 Oi(t){let n=(await Ee()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Yf({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=Jf(r),c=await Ee(),l={id:$x().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($i,{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 Zf(t,{md:e,note:n}={}){let o=(await Ee()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");zf(o,"update");let s=Jf(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await sn.mkdir($i,{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 Qf(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 zf(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 tp(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 ep(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 np(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 rp from"node:fs/promises";import Yo from"node:path";import{randomUUID as Mx}from"node:crypto";var sp=".bostrat/uploads",Ci=t=>Yo.join(t,sp),op=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,Px=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Nx=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 Mi(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return Px.has(e)?"image":Nx.has(e)?"doc":null}function Bx(t){let r=Yo.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 ap(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>op)throw new Error(`file too large \u2014 max ${Math.floor(op/(1024*1024))} MB`);let r=Bx(e),o=Yo.extname(r).slice(1),s=Mi(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Mx().slice(0,8),i=Ci(t);await rp.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await rp.writeFile(Yo.join(i,c),n),{id:a,name:r,rel:`${sp}/${c}`,kind:s,size:n.length}}function ip(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var Dx=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Pi(t){return t.role==="user"&&Dx.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 Ux=Lx(jx),Ie=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),mp=U.join(Ie,"tracks.json"),Fx=U.join(Ie,"events.jsonl"),Fi=U.join(Ie,"workspaces"),Hx=process.env.BOSTRAT_DEFAULT_REPO||null,es={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"}},Wi=Object.keys(es);function Wx(t,{skipPermissions:e=!0}={}){let n=es[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 hp(t,e,{skipPermissions:n=!0}={}){let r=es[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 ns(t){let e=es[t];if(e)return process.env[e.envVar]||e.base}async function rs(t,e=!0){let n=Wx(t,{skipPermissions:e});if(!await Us(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 os=".bostrat/handoff/latest.md";function qx(t,e=os){return`${t.trim()}
75
75
 
76
- (Read ${e} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}var qx=/\(Read \S{1,300} for handoff context from the prior lead — you are now the lead; none of the prior chat is in your context\. Then respond to the user above\.\)$/;function ip(t){return t.role==="user"&&qx.test((t.text||"").trim())}var Hi=".bostrat/handoff/subtask-result.md",Yo=".bostrat/handoff/lead-context.md",Gx=t=>`You are a SUBTASK agent working in an isolated git worktree, delegated by a lead agent on a shared workstream. The lead's full working tree is already checked out here, and the conversation so far is summarized in ${Yo} \u2014 read that first for context. Your scoped task:
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 Gx=/\(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 cp(t){return t.role==="user"&&Gx.test((t.text||"").trim())}var qi=".bostrat/handoff/subtask-result.md",Zo=".bostrat/handoff/lead-context.md",Kx=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 ${Zo} \u2014 read that first for context. Your scoped task:
77
77
 
78
78
  ${t}
79
79
 
80
- Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${Hi} with sections \u2014 Result (what you did), Files (paths you changed), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`,Kx=(t,e,n,r)=>`A subtask box you delegated has finished: "${t}". Its changes are now merged into your working tree (${e.files} file${e.files===1?"":"s"}, +${e.insertions} \u2212${e.deletions}). Read ${r} for exactly what it did. `+(n.length?`IMPORTANT: ${n.length} merge conflict${n.length===1?"":"s"} need resolving \u2014 fix the conflict markers in: ${n.join(", ")}, then continue.`:"Review the merged changes and continue the work."),Yt=null,Mi=null;async function H(){return Yt||(Mi||=(async()=>{try{Yt=JSON.parse(await P.readFile(pp,"utf8"))}catch{Yt={tracks:[]}}finally{Mi=null}return Yt})(),Mi)}async function K(){await qt(pp,Yt)}var Q=(t,e)=>t.tracks.find(n=>n.id===e)||null,Pi=Promise.resolve();function Vx(t,e){let n=JSON.stringify({trackId:t,...e})+`
81
- `;return Pi=Pi.then(()=>P.mkdir(Ie,{recursive:!0})).then(()=>P.appendFile(Ux,n)).catch(()=>{}),Pi}var it=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),Vx(t.id,r),id(t,r),r};bl(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await H()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(it(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await K().catch(()=>{}))});var Ni=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),Jx=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function hp(t){if(!t)return null;let e=typeof t=="string"?[t]:Array.isArray(t?.codes)&&t.codes.length?t.codes:t?.code?[t.code]:[],n=[...new Set(e.filter(s=>Jx.has(s)))];if(!n.length)return null;let r=typeof t=="object"?t?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function wp(t,e,n,{kind:r}={}){let o=await G(e);if(!o)throw new Error(`box "${e}" is not running`);let s=es(o.agent);await we({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await yt(e,n,{...re(o.agent),kind:r||"orchestration"}),await L({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await H(),i=Q(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);it(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await K()}var an=new Set(["merged","conflict","canceled","failed","handed-off"]),cp=new Map;async function zx(t){let e=U.join(xo(t),`${t.id}.ndjson`),n;try{n=await P.stat(e)}catch{return null}let r=cp.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return cp.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Xx(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Ja(n.name,n.agent)}async function Re(t){let e=new Map((await vn()).map(a=>[a.name,a])),n=t.boxes.map(a=>{let i=e.get(a.name),c=an.has(a.state)?a.state:i?a.role==="lead"?"live":a.state:"stopped";return{...a,state:c,cwd:i?.cwd||a.cwd,agent:i?.agent||a.agent,sessionId:i?.sessionId||a.sessionId}}),[r,{working:o,prompted:s}]=await Promise.all([zx(t),Xx(t,n)]);return{...t,boxes:n,lastResponseAt:r,working:o,prompted:s}}var Yx=5e3;function Zx(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-Yx,f=r[u+1]?.createdAt??1/0,w=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);w&&(s.add(w.id),c.push({box:l.name,sessionId:w.id}))}return c}async function gp(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(f=>f.box.sessionId),a=pl(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=Zx(u,a,f=>i.has(f));for(let f of d){let w=u.find(m=>m.name===f.box);w&&(w.sessionId=f.sessionId),await ol(f.box,f.sessionId),n=!0}}return n}async function dt(){let t=await H();return await gp(t)&&await K(),Promise.all(t.tracks.map(Re))}async function yp(t,e){let n=await H();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function rt(t){let e=await H(),n=Q(e,t);return n?(await gp(e)&&await K(),Re(n)):null}var Qx={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function lp(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(Qx[t.agent]||[]).some(n=>n.test(e))}var t0=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function up(t){let e=(t.text||"").trim();return e?t0.some(n=>n.test(e)):!1}var e0=10*6e4;async function Wi(t,e={}){let n=await rt(t);if(!n)return null;let r=new Set([...n.events.filter(y=>y.type==="orchestration-prompt"&&y.prompt).map(y=>String(y.prompt).trim()),...n.events.filter(y=>y.type==="said"&&y.text).map(y=>String(y.text).trim())]),o=Nr(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=fi(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...of(n),...l],f={windows:[...$l(n),...Ol(n,uo),...a,...u]},m=(await Promise.all(n.boxes.map(y=>gu(y,e)))).flat(),g=m.map(y=>{let j={kind:"message",...y};if(j.role==="assistant"&&(j.text||"").trim()&&j.tools?.length){let wn=gr(j.agent).cliTools,Yr=j.tools.filter(je=>!wn.has(je));return{...j,tools:Yr.length?Yr:void 0}}return j}).map(y=>{if(y.role!=="user")return y;let j=No(y.text);return j?j.kind==="suppress"?null:{...y,note:j,text:jd(j)}:y}).filter(Boolean).filter(y=>!(y.role==="user"&&r.has((y.text||"").trim()))).filter(y=>!lp(y)).filter(y=>!up(y)).filter(y=>!ip(y)).filter(y=>!Ci(y)).filter(y=>y.role!=="assistant"||!!(y.text||"").trim()||!!y.images?.length).filter(y=>!Cl(y,f)),x=g.filter(y=>y.failed&&y.text===Sa);if(x.length){let y=new Map;for(let j of x){if(!y.has(j.box)){let je=[...(await pt(j.box,{lines:Rt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map($s=>$s[1]);y.set(j.box,je.length?je[je.length-1]:null)}let wn=y.get(j.box);wn&&(j.text=`\u25A0 ${wn}`)}}let A=await Promise.all(n.boxes.map(y=>bu(y,e)));await Bd(n,A.flat()),await Pd(n,g),await cd(n),await ff(n,g).catch(()=>{});for(let y of n.boxes)y.agent==="codex"&&y.state==="live"&&await Hd(n,y),y.agent==="cursor"&&y.state==="live"&&await Vd(n,y);let S=await Promise.all(n.boxes.map(y=>yu(y,e)));await Nd(n,S.flat().filter(y=>!Ni(y.box,y.ts,u)));let T=await B(n),E=c&&i.viewId?i.viewId:null,J=gd(T),M=Bo(T).filter(y=>!(y.kind==="message"&&(lp(y)||up(y)||ip(y)||Ci(y)))&&!(y.kind==="view-card"&&E&&y.viewId===E)),I=yd(M,J),h=M.filter(y=>!(y.kind==="message"&&y.role==="assistant"&&!I.has(y)&&Ni(y.box,y.ts,J))),p=ei(T,h).map(y=>({kind:"command-batch",id:y.id,ts:y.pending?y.ts:(y.anchorTs??y.ts)-1,anchorTs:y.anchorTs,anchorBox:y.anchorBox,agent:y.agent,boxRole:y.boxRole,commands:y.commands,pending:y.pending})).filter(y=>!Ni(y.anchorBox,y.anchorTs??y.ts,u)),b=[...h,...p].filter(y=>y.ts!=null).sort((y,j)=>y.ts-j.ts),v=n0(n),ot=v.size?b.filter(y=>y.kind==="event"||!v.has(y.box??y.anchorBox)):b,$=n.boxes.find(y=>y.name===n.leadBox)||null,z=$?await cr($.cwd,$.sessionId,{fallbackNewest:!1}):null;if(!z&&$){let y=[...T].reverse().find(j=>j.type==="context.usage"&&j.boxId===$.name);y?.payload?.window&&(z={pct:y.payload.pct??(y.payload.occupancy!=null?y.payload.occupancy/y.payload.window:null),occupancy:y.payload.occupancy??null,window:y.payload.window,windowKnown:!0,model:y.payload.model||null})}let N=[...m].reverse().find(y=>y.role==="assistant"&&y.model),W=N?.model||null,_=[...n.events||[]].reverse().find(y=>y.type==="model-change"),q=z?.model||W;_&&(!N?.ts||_.at>=N.ts)&&(q=_.to);let X=$?await pt($.name,{lines:Rt}).catch(()=>""):"",lt=$&&Cn($.agent,X)?await me($.name).catch(()=>X):X,et=$?Xe($,lt):null;et&&ld(n,$,et);let wt=!!$&&!et&&ur($.agent,X);$&&!$.readyAt&&(wt||et||X&&io($.agent,X))&&($.readyAt=Date.now(),await K());let Mt=!!$&&!$.readyAt&&Date.now()-(n.createdAt||0)<e0;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&$&&io($.agent,X)&&!et){let y=await H(),j=Q(y,n.id);j?.setup&&!j.setup.connectedAt&&(j.setup.connectedAt=Date.now(),n.setup.connectedAt=j.setup.connectedAt,await R(j,{type:"provider.connected",actor:"system",source:"pane",boxId:$.name,provider:$.agent,payload:{provider:$.agent,ts:j.setup.connectedAt}}).catch(()=>{}),await K())}let De=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Dt=null;if(c){let y=Date.now()-i.startedAt;if(i.viewId&&!wt){let j=Ho(n.id);j&&(it(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:j.finishedAt}),await K())}else if(et||y>nf&&!wt&&!i.viewId||y>di){let j=et?"needs-input":y>di?"timeout":"no-view";Ho(n.id,{error:j})&&await R(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:$?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:j}}).catch(()=>{})}else Dt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ot,context:z?{pct:z.pct,occupancy:z.occupancy,window:z.window,windowKnown:z.windowKnown,model:z.model}:null,models:{current:q,options:td($?.agent)},pendingPrompt:et,working:wt,leadStarting:Mt,handoff:De,view:Dt,queued:o0(n.id)}}function n0(t){let e=new Set;for(let n of t.boxes||[])n.role==="subtask"&&n.name&&e.add(n.name);for(let n of Object.values(t.subtasks||{}))n?.box&&e.add(n.box);return e}async function bp(t,e){let n=await rt(t);if(!n)return null;let r=Jn(t).find(d=>d.id===e)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await B(n),a=Bo(s).filter(d=>d.box===o),i=ei(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await pt(o,{lines:Rt}).catch(()=>"");u=ur(l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function qi(t,{promptId:e,answers:n,text:r}={}){let o=Q(await H(),t);if(!o)throw new Error(`track "${t}" not found`);let s=o.boxes.find(f=>f.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await G(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Pa(s);if(!i)throw new Error("no pending prompt to answer");if(e&&e!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let f=typeof r=="string"?r.trim():"";if(!f)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(f)||f.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await yt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await gt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.provider==="claude"||i.answerMode==="keys"){let f=Nu(i,n||[]);await gt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=Tu(i,n||[]);await yt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:f,count:1}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(f=>l.options?.[f]).filter(Boolean).map(f=>({label:f.label,description:f.description||""})),d=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:u,...d?{control:d}:{}},await L({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),ud(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function kp(t,{promptId:e,questionIndex:n}={}){let r=Q(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await G(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Pa(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(e&&e!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=Bu(a,n);return c.length?(await gt(r.leadBox,c,{kind:"navigate-prompt"}),await L({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}var r0=1500,$r=new Map;function xp(t){let e=$r.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},$r.set(t,e)),e}function o0(t){return($r.get(t)?.items??[]).map(e=>({...e}))}function os(t){let e=$r.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}function s0(t){let e=xp(t);e.timer||(e.timer=setInterval(()=>a0(t).catch(()=>{}),r0))}async function a0(t){let e=$r.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Q(await H(),t);if(!n||n.archivedAt){os(t);return}let r=Nr(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){qn(e,!1);return}let o=await G(n.leadBox);if(!o){qn(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await pt(n.leadBox,{lines:Rt}).catch(()=>"");if(ao(s?.agent,a)){qn(e,!1);return}if(Cn(s?.agent,a)){let c=await me(n.leadBox).catch(()=>a);if(Xe(s,c)){qn(e,!1);return}}if(!qn(e,!0))return;let i=e.items.shift();e.idleSince=null,await Sp(n,s,o,i.text,i.kind??null,i.attachments??null),await K()}finally{e.flushing=!1}}}async function Sp(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=Wx(r,t.pendingHandoffHydrate.artifact||rs),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(l=>l&&l.rel):[];if(c.length){let l=ap(c.map(u=>u.rel));a=a.trim()?`${a.trim()}
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 ${qi} 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.`,Vx=(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,Ni=null;async function H(){return Yt||(Ni||=(async()=>{try{Yt=JSON.parse(await P.readFile(mp,"utf8"))}catch{Yt={tracks:[]}}finally{Ni=null}return Yt})(),Ni)}async function K(){await qt(mp,Yt)}var Q=(t,e)=>t.tracks.find(n=>n.id===e)||null,Bi=Promise.resolve();function Jx(t,e){let n=JSON.stringify({trackId:t,...e})+`
81
+ `;return Bi=Bi.then(()=>P.mkdir(Ie,{recursive:!0})).then(()=>P.appendFile(Fx,n)).catch(()=>{}),Bi}var it=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),Jx(t.id,r),cd(t,r),r};kl(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 Di=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),zx=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function wp(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=>zx.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 gp(t,e,n,{kind:r}={}){let o=await G(e);if(!o)throw new Error(`box "${e}" is not running`);let s=ns(o.agent);await we({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await yt(e,n,{...ne(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"]),lp=new Map;async function Xx(t){let e=U.join(So(t),`${t.id}.ndjson`),n;try{n=await P.stat(e)}catch{return null}let r=lp.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 lp.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Yx(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Xa(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([Xx(t),Yx(t,n)]);return{...t,boxes:n,lastResponseAt:r,working:o,prompted:s}}var Zx=5e3;function Qx(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-Zx,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 yp(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=ml(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=Qx(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 sl(f.box,f.sessionId),n=!0}}return n}async function dt(){let t=await H();return await yp(t)&&await K(),Promise.all(t.tracks.map(Re))}async function bp(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 yp(e)&&await K(),Re(n)):null}var t0={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function up(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(t0[t.agent]||[]).some(n=>n.test(e))}var e0=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function dp(t){let e=(t.text||"").trim();return e?e0.some(n=>n.test(e)):!1}var n0=10*6e4;async function Gi(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=Br(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=mi(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...sf(n),...l],f={windows:[...Ol(n),...Cl(n,fo),...a,...u]},m=(await Promise.all(n.boxes.map(y=>yu(y,e)))).flat(),g=m.map(y=>{let j={kind:"message",...y};if(j.role==="assistant"&&(j.text||"").trim()&&j.tools?.length){let wn=yr(j.agent).cliTools,Zr=j.tools.filter(je=>!wn.has(je));return{...j,tools:Zr.length?Zr:void 0}}return j}).map(y=>{if(y.role!=="user")return y;let j=Bo(y.text);return j?j.kind==="suppress"?null:{...y,note:j,text:Ld(j)}:y}).filter(Boolean).filter(y=>!(y.role==="user"&&r.has((y.text||"").trim()))).filter(y=>!up(y)).filter(y=>!dp(y)).filter(y=>!cp(y)).filter(y=>!Pi(y)).filter(y=>y.role!=="assistant"||!!(y.text||"").trim()||!!y.images?.length).filter(y=>!Ml(y,f)),x=g.filter(y=>y.failed&&y.text===Ta);if(x.length){let y=new Map;for(let j of x){if(!y.has(j.box)){let je=[...(await pt(j.box,{lines:$t}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Os=>Os[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=>ku(y,e)));await Dd(n,A.flat()),await Nd(n,g),await ld(n),await pf(n,g).catch(()=>{});for(let y of n.boxes)y.agent==="codex"&&y.state==="live"&&await Wd(n,y),y.agent==="cursor"&&y.state==="live"&&await Jd(n,y);let S=await Promise.all(n.boxes.map(y=>bu(y,e)));await Bd(n,S.flat().filter(y=>!Di(y.box,y.ts,u)));let T=await B(n),E=c&&i.viewId?i.viewId:null,J=yd(T),M=Do(T).filter(y=>!(y.kind==="message"&&(up(y)||dp(y)||cp(y)||Pi(y)))&&!(y.kind==="view-card"&&E&&y.viewId===E)),I=bd(M,J),h=M.filter(y=>!(y.kind==="message"&&y.role==="assistant"&&!I.has(y)&&Di(y.box,y.ts,J))),p=ri(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=>!Di(y.anchorBox,y.anchorTs??y.ts,u)),b=[...h,...p].filter(y=>y.ts!=null).sort((y,j)=>y.ts-j.ts),v=r0(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 lr($.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:$t}).catch(()=>""):"",lt=$&&Cn($.agent,X)?await me($.name).catch(()=>X):X,nt=$?Xe($,lt):null;nt&&ud(n,$,nt);let wt=!!$&&!nt&&dr($.agent,X);$&&!$.readyAt&&(wt||nt||X&&co($.agent,X))&&($.readyAt=Date.now(),await K());let Pt=!!$&&!$.readyAt&&Date.now()-(n.createdAt||0)<n0;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&$&&co($.agent,X)&&!nt){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,jt=null;if(c){let y=Date.now()-i.startedAt;if(i.viewId&&!wt){let j=Wo(n.id);j&&(it(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:j.finishedAt}),await K())}else if(nt||y>rf&&!wt&&!i.viewId||y>pi){let j=nt?"needs-input":y>pi?"timeout":"no-view";Wo(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 jt={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:ed($?.agent)},pendingPrompt:nt,working:wt,leadStarting:Pt,handoff:De,view:jt,queued:s0(n.id)}}function r0(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 kp(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=Do(s).filter(d=>d.box===o),i=ri(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:$t}).catch(()=>"");u=dr(l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Ki(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 Ba(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=Bu(i,n||[]);await gt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=Au(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(()=>{}),dd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function xp(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 Ba(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=Du(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 o0=1500,Or=new Map;function Sp(t){let e=Or.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},Or.set(t,e)),e}function s0(t){return(Or.get(t)?.items??[]).map(e=>({...e}))}function ss(t){let e=Or.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 a0(t){let e=Sp(t);e.timer||(e.timer=setInterval(()=>i0(t).catch(()=>{}),o0))}async function i0(t){let e=Or.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){ss(t);return}let r=Br(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:$t}).catch(()=>"");if(io(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 vp(n,s,o,i.text,i.kind??null,i.attachments??null),await K()}finally{e.flushing=!1}}}async function vp(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=qx(r,t.pendingHandoffHydrate.artifact||os),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(l=>l&&l.rel):[];if(c.length){let l=ip(c.map(u=>u.rel));a=a.trim()?`${a.trim()}
82
82
 
83
- ${l}`:l}await we({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:es(n.agent)}),await yt(t.leadBox,a,{...re(e?.agent),kind:i?"lead-handoff-first-prompt":"say"}),i&&await L({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length}),it(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...c.length?{attachments:c}:{}}),delete t.draft}async function $e(t,e,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(f=>f&&typeof f.rel=="string"):[];if((typeof e!="string"||!Mn(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Q(await H(),t);if(!s)throw new Error(`track "${t}" not found`);if(s.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before driving it`);let a=await G(s.leadBox);if(!a&&await Vi(s)&&(await K(),a=await G(s.leadBox)),!a)throw new Error(`lead box "${s.leadBox}" is not running`);let i=s.boxes.find(f=>f.name===s.leadBox),c=Mn(e),l=xp(t),u=await pt(s.leadBox,{lines:Rt}).catch(()=>""),d=l.items.length>0||ao(i?.agent,u);if(!d&&Cn(i?.agent,u)){let f=await me(s.leadBox).catch(()=>u);d=!!Xe(i,f)}if(d){let f={id:Qo(),text:c,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(f),s0(t),delete s.draft,await K(),{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:l.items.length}}return await Sp(s,i,a,c,n,o),await K(),{ok:!0,leadBox:s.leadBox}}var dp="Spin up a live preview View of this app so I can see it on another device. From the repo root, run `bostrat-view up --name \"<short name>\" -- <serve command>` \u2014 pick the right dev/serve command for this repo yourself (e.g. `npm run dev`), pass $PORT through if the command needs an explicit port, and share the public URL it prints. If a suitable View is already live, just re-share its URL instead of starting another. Don't paste a localhost link \u2014 it's dead on my device.\n\nIMPORTANT: if you cannot confidently identify WHICH project/app to serve, or which serve command to use (e.g. several apps in this tree, or no obvious dev script), do NOT guess and do NOT run `bostrat-view up`. Instead, reply with a short question asking me which one you should preview, and stop.";async function vp(t,{hint:e}={}){let n=Q(await H(),t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it first`);let r=fi(n.id);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o=n.boxes.find(i=>i.name===n.leadBox),s=String(e||"").trim()?String(e).trim().slice(0,40):"preview";rf(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${dp}
83
+ ${l}`:l}await we({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:ns(n.agent)}),await yt(t.leadBox,a,{...ne(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 zi(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=Sp(t),u=await pt(s.leadBox,{lines:$t}).catch(()=>""),d=l.items.length>0||io(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:ts(),text:c,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(f),a0(t),delete s.draft,await K(),{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:l.items.length}}return await vp(s,i,a,c,n,o),await K(),{ok:!0,leadBox:s.leadBox}}var fp="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 Tp(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=mi(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";of(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${fp}
84
84
 
85
- What to preview: ${String(e).trim()}`:dp;try{await $e(t,a,{kind:"spin-up-view"})}catch(i){throw Ho(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Gi(t){let e=Q(await H(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);let n=e.boxes.find(i=>i.name===e.leadBox),r=os(t),o=await G(e.leadBox);o&&await gt(e.leadBox,["Escape"],{kind:"interrupt"});let s=o?await l0(e,n):null,a=o?await i0(e).catch(()=>null):null;return await L({flow:"interrupt",phase:"sent",trackId:t,sessionName:e.leadBox,agent:n?.agent,dropped:r.length,unsent:!!s,shipDismissed:!!a}).catch(()=>{}),{ok:!0,interrupted:!!o,dropped:r.length,restored:s}}async function i0(t){let e=vi(await B(t));return e?.offerId?(await R(t,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,action:"dismiss",actions:null,reason:"interrupt"}}),e.offerId):null}var c0=[250,400,500,600];async function l0(t,e){if(e?.agent!=="claude")return null;let n=[...t.events||[]].reverse().find(r=>r.type==="said"&&r.text);if(!n)return null;for(let r of c0){await Qi(r);let o=await pt(t.leadBox,{lines:Rt}).catch(()=>""),s=Na(e.agent,o);if(s&&ju(n.text,s))return await u0(t.leadBox,e.agent,n.text.length+8),t.draft={text:n.text,updatedAt:Date.now()},it(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),await K(),n.text}return null}async function u0(t,e,n){for(let r=0;r<3;r++){await no(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Qi(200);let o=await pt(t,{lines:Rt}).catch(()=>""),s=Na(e,o);if(!s)return!0;n=s.length+8}return!1}async function Tp(t,e){if(typeof e!="string")throw new Error("text (string) required");let n=Q(await H(),t);if(!n)throw new Error(`track "${t}" not found`);let r=Mn(e);return r?n.draft={text:r,updatedAt:Date.now()}:delete n.draft,await K(),{ok:!0,draft:n.draft||null}}async function Ap(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Q(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await G(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=ed(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await cr(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await we({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:es(o.agent)});let u=await nd(a,r.leadBox,i);return it(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await K(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function _p(t){for(let e of t){if(!e||!await ja(e))continue;let n=await Uu(e).catch(()=>null);if(!n)continue;if((await P.stat(U.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function d0(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:U.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function f0(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await ua(e));let n=await _p([e]);if(!n)throw new Error(`tagged repo "${t}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function p0(t,e,n,r){let o=`track/${Xi(e)}-${t}`,s=[];try{await P.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await f0(i);if(s.some(w=>w.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Y(c).catch(()=>null),u=U.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let w=2;a.has(u);w++)u=`${U.basename(c)}-${w}`;a.add(u);let d=U.join(n,u),f=await La(c);await yo(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await g0(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Ze(i.base,i.dir).catch(()=>{});throw await P.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function m0(t,e,{repo:n,repos:r,cwd:o}={}){await P.mkdir(Li,{recursive:!0});let s=U.join(Li,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return p0(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await ua(n));let i=await _p([n,o,Fx]);if(i){let c=`track/${Xi(e)}-${t}`;try{let l=await La(i);await yo(i,s,c,l),await Ui(s);let u=await Y(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await P.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await P.mkdir(s,{recursive:!0}),await Wu(s),await Ui(s),{dir:s,base:null,branch:null,kind:"repo"}}var Or="# bostrat workspace",Rr=`${Or}
85
+ What to preview: ${String(e).trim()}`:fp;try{await $e(t,a,{kind:"spin-up-view"})}catch(i){throw Wo(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Vi(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=ss(t),o=await G(e.leadBox);o&&await gt(e.leadBox,["Escape"],{kind:"interrupt"});let s=o?await u0(e,n):null,a=o?await c0(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 c0(t){let e=Ai(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 l0=[250,400,500,600];async function u0(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 l0){await ec(r);let o=await pt(t.leadBox,{lines:$t}).catch(()=>""),s=Da(e.agent,o);if(s&&Lu(n.text,s))return await d0(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 d0(t,e,n){for(let r=0;r<3;r++){await ro(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await ec(200);let o=await pt(t,{lines:$t}).catch(()=>""),s=Da(e,o);if(!s)return!0;n=s.length+8}return!1}async function Ap(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 _p(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=nd(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await lr(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:ns(o.agent)});let u=await rd(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 Ep(t){for(let e of t){if(!e||!await Ua(e))continue;let n=await Fu(e).catch(()=>null);if(!n)continue;if((await P.stat(U.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function f0(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 p0(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await fa(e));let n=await Ep([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 m0(t,e,n,r){let o=`track/${Zi(e)}-${t}`,s=[];try{await P.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await p0(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 Fa(c);await bo(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await y0(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 h0(t,e,{repo:n,repos:r,cwd:o}={}){await P.mkdir(Fi,{recursive:!0});let s=U.join(Fi,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return m0(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await fa(n));let i=await Ep([n,o,Hx]);if(i){let c=`track/${Zi(e)}-${t}`;try{let l=await Fa(i);await bo(i,s,c,l),await Hi(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 qu(s),await Hi(s),{dir:s,base:null,branch:null,kind:"repo"}}var Cr="# bostrat workspace",$r=`${Cr}
86
86
 
87
87
  When asked to run, preview, or demo this app so the operator can review it: do NOT
88
88
  start an ad-hoc dev server and do NOT share a localhost URL (the operator is on
@@ -188,9 +188,9 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
188
188
  statement, or omitted entirely renders no controls \u2014 so close EVERY completed
189
189
  implementation turn this way, every time. When the operator has already asked you
190
190
  to commit / push / open the PR, just do it; do not re-offer.
191
- `;async function Ui(t){try{await P.writeFile(U.join(t,"AGENTS.md"),Rr,{flag:"wx"})}catch{return}await y0(t,["/AGENTS.md"])}var Bi=t=>typeof t=="string"&&(t===Or||t.startsWith(`${Or}
192
- `)),h0="# bostrat multi-repo workspace";function w0(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
193
- `);return`${h0}
191
+ `;async function Hi(t){try{await P.writeFile(U.join(t,"AGENTS.md"),$r,{flag:"wx"})}catch{return}await b0(t,["/AGENTS.md"])}var ji=t=>typeof t=="string"&&(t===Cr||t.startsWith(`${Cr}
192
+ `)),w0="# bostrat multi-repo workspace";function g0(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`${w0}
194
194
 
195
195
  This directory is NOT a git repo. It is a bostrat Track workspace holding one
196
196
  git worktree per tagged repo, ALL on the same branch \`${e}\`:
@@ -202,22 +202,22 @@ push / open PRs) in each repo separately, always on that shared branch. Work
202
202
  only needs to land in the repos it actually touches \u2014 an untouched repo's
203
203
  branch simply stays at its fork point and never gets pushed. When asked to run
204
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}
205
+ `}async function y0(t,e,n){let r=g0(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await P.writeFile(U.join(t,o),r).catch(()=>{})}var Li=$r.replace(`${Cr}
206
+ `,`${Cr}
207
207
 
208
208
  (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
209
209
  SCOPE: these instructions apply ONLY when your working directory is under
210
210
  ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
211
211
  When working anywhere else, ignore everything below.
212
- `);async function Ep(t){let{stdout:e}=await Lx("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function y0(t,e){try{let n=await Ep(t);await P.mkdir(U.dirname(n),{recursive:!0});let r=await P.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
212
+ `);async function Ip(t){let{stdout:e}=await Ux("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function b0(t,e){try{let n=await Ip(t);await P.mkdir(U.dirname(n),{recursive:!0});let r=await P.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
213
213
  `).includes(s));o.length&&await P.appendFile(n,(r.endsWith(`
214
214
  `)||!r?"":`
215
215
  `)+o.join(`
216
216
  `)+`
217
- `)}catch{}}async function b0(t,e){try{let n=await Ep(t),r=await P.readFile(n,"utf8"),o=r.split(`
217
+ `)}catch{}}async function k0(t,e){try{let n=await Ip(t),r=await P.readFile(n,"utf8"),o=r.split(`
218
218
  `).filter(s=>s!==e);o.length!==r.split(`
219
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}
220
+ `))}catch{}}async function Rp(){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),$r).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,Li,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):ji(o)&&o!==Li&&(await P.writeFile(r,Li).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Fi,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&&ji(c)&&c!==$r&&(await P.writeFile(i,$r).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await P.readFile(l,"utf8").catch(()=>null);u!==null&&ji(u)&&(await P.rm(l,{force:!0}).catch(()=>{}),await k0(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function x0(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of f0(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 Ji({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 rs(o,s),i=await H(),c=ts().slice(0,8),l=await h0(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(Fs(d))&&(d=`${t.trim().slice(0,50).trim()} ${c} lead`);let f=await Ue(d,l.dir,{agentCmd:a,agent:o});lo({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:[Bp(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 S0=new Set(["claude","codex","cursor"]);async function $p({provider:t="claude",fresh:e=!1}={}){if(!S0.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 rs(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=ts().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"&&lo({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:[Bp(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 Op(t,e,n){await ff(t).catch(()=>{});let r=Mr.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Mr.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 tc(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 Cp(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 Wf(s),await Op(t,s,"track deleted"),n?await np(t).catch(()=>{}):await ep(t).catch(()=>{}),await id(),await x0(s),e&&await qf(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 Op(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 zi(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=hp(e.agent,e.sessionId);if(!await Us(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}),lo({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 Mp(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 zi(n),await K(),Re(n)}async function Pp(){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 Np(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:$t}).catch(()=>"");if(io(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 zi(n);if(await K(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Re(n)}var Bp=(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 Dp(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 Mr=new Map,Br=t=>Mr.get(t)||(Yt?Q(Yt,t)?.handoff:null)||null;async function Qo(t,e){let n=await H(),r=Q(n,t);r&&(r.handoff={...r.handoff||{},...e},await K())}function v0(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 jp(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=v0(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=Dp(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:os,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 Xi(t,e,n=!0,r=null){let o=Mr.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 rs(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:wp(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Mr.set(t,c),await Qo(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),T0(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 Qo(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 T0(t,e,n,r,o){let s=async(T,E={})=>{t.step=T,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${T}`),await Qo(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 Bl(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await sa(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:ns(o)});let f=await pt(t.fromBox,{lines:$t}).catch(()=>"");if(!co(o,f)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let T=hp(c,l,{skipPermissions:t.skipPermissions});await sa(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=Dp(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:os,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 Qo(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var A0=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,_0=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Kn=U.join(Ie,"worktrees"),Ui=[Zo,qi],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)},Yi=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 E0(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 Pr=t=>E0(t.trackId,t.id,Yi(t));async function Gn(t,e){t.step=e,await Pr(t)}function I0(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 R0(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function $0(t,e,n){if(!R0(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),Wp(s,o).catch(a=>Fp(s,a)),!0}async function Lp(){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 $0(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=I0(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 pp(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 pp(n,r.name,o)}}return e=await O0(t)||e,e&&await K(),e}async function pp(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 O0(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 Zi=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Nr=class extends Error{};async function Up(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 rs(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 Ua(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let c=ts().slice(0,8),l={id:c,trackId:t,trackName:a.name,agent:e,task:Mn(n),skipPermissions:r,reason:wp(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${Zi(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 Pr(l),C0(l,i,s).catch(u=>Fp(l,u)),l}async function Fp(t,e){t.failedStep=t.step,t.error=e instanceof Nr?"canceled":e?.message||String(e),t.step=e instanceof Nr?"canceled":"error",t.finishedAt=Date.now(),await tc(t).catch(()=>{}),await Pr(t).catch(()=>{}),await B0(t).catch(()=>{})}function Qi(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 Hp(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 C0(t,e,n){await Gn(t,"snapshot"),t.base=await Hu(e.cwd,`bostrat: snapshot for subtask ${t.id}`),await Gn(t,"spawn"),await P.mkdir(Kn,{recursive:!0}),await bo(e.cwd,t.dir,t.branch,t.base),await Hi(t.dir),await M0(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 P0(t,o),await gp(t.trackId,o.name,Kx(t.task),{kind:"subtask"}),await Wp(t,e)}async function Wp(t,e){await Gn(t,"working");let n=U.join(t.dir,qi);if(await j0(n,t),t.canceled)throw new Nr;await Gn(t,"merge"),await Wu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Gu(e.cwd,t.base,t.branch,Ui),t.files=await Ku(e.cwd,t.base,t.branch,Ui);let r=await Vu(e.cwd,t.base,t.branch,Ui);t.applied=r.applied,t.conflicts=r.conflicts,await Pr(t);let o=await D0(t,e,n);await Gn(t,"resume"),await gp(t.trackId,t.leadBox,Vx(t.task,t.stat,t.conflicts,o),{kind:"subtask-resume"}),await Gn(t,"reap"),await tc(t),t.step="done",t.finishedAt=Date.now(),await N0(t),await Pr(t)}async function M0(t,e){let n=U.join(t.dir,U.dirname(Zo));await P.mkdir(n,{recursive:!0});let r="";try{r=await wl(e.cwd,e.sessionId)}catch{}if(!r)try{r=await P.readFile(U.join(e.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${t.id}
221
221
 
222
222
  **Delegated task:** ${t.task}
223
223
 
@@ -228,53 +228,53 @@ When working anywhere else, ignore everything below.
228
228
  ## Conversation so far (lead \u2192 you)
229
229
 
230
230
  ${r}
231
- `;await P.writeFile(U.join(t.dir,Yo),o)}async function M0(t,e){let n=await H(),r=Q(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),it(r,"subtask-delegated",{to:e.name,toAgent:t.agent,task:t.task,subtaskId:t.id,skipPermissions:t.skipPermissions,reason:t.reason}),r.subtasks=r.subtasks||{},r.subtasks[t.id]={...r.subtasks[t.id]||{},...zi(t)},await K())}async function P0(t){let e=await H(),n=Q(e,t.trackId);if(!n)return;let r=n.boxes.find(o=>o.subtaskId===t.id);r&&(r.state=t.conflicts.length?"conflict":"merged",r.stat=t.stat),it(n,"subtask-merged",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,stat:t.stat,conflicts:t.conflicts.length,files:t.files,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),await K()}async function N0(t){let e=await H(),n=Q(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),it(n,r?"subtask-canceled":"subtask-failed",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,failedStep:t.failedStep||t.step,error:t.error,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[t.id]={...n.subtasks[t.id]||{},...zi(t)},await K()}async function B0(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=U.join(e.cwd,r);await P.mkdir(U.dirname(o),{recursive:!0}),await P.copyFile(n,o)}catch{}return r}async function Zi(t){if(t.box&&await Wt(t.box).catch(()=>{}),t.dir){let n=(await G(t.leadBox).catch(()=>null))?.cwd;n&&await Ze(n,t.dir).catch(()=>{})}}async function D0(t,e){let n=(e.startedAt||Date.now())+T0;for(;;){if(e.canceled||e.forceMerge)return;try{await P.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Qi(A0)}}function Qi(t){return new Promise(e=>setTimeout(e,t))}import Fr from"node:fs/promises";import ws from"node:path";import _S from"node:os";import j0 from"node:fs/promises";import Wp from"node:path";import L0 from"node:os";var qp=()=>Wp.join(process.env.BOSTRAT_STATE_DIR||Wp.join(process.env.HOME||L0.tmpdir(),".bostrat"),"integrations.json");async function cn(){try{return JSON.parse(await j0.readFile(qp(),"utf8"))||{}}catch{return{}}}var ss=["linked","prOpened","prMerged","create"],tc=["prompt","auto","off"],U0={create:["prompt","off"]},Gp=t=>U0[t]??tc;function as(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of ss){let s=Gp(o),a=s.includes(n)?n:"prompt",i=t.ticketFlow?.[o]??e.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function ln(){let t=await cn(),e=process.env.BOSTRAT_LINEAR_API_KEY||t.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||t.linear?.defaultTeam||null;return e?{apiKey:e,ticketFlow:as(t),defaultTeam:n}:null}async function is(){let t=await cn(),e=t.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||e.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||e.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||e.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:e.tokenExpiresAt??null,ticketFlow:as(t)}}async function ec(){return as(await cn())}async function Kp(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${ss.join(", ")}`);for(let[r,o]of e){if(!ss.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${ss.join(", ")}`);let s=Gp(r);if(!s.includes(o))throw new Error(s.length<tc.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${tc.join(", ")}`)}let n=await cn();return n.ticketFlow={...as(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await qt(qp(),n,{mode:384}),n.ticketFlow}async function Br(){let t=(await cn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function Vp(){let t=(await cn()).cedar||{},e=process.env.BOSTRAT_CEDAR_INGEST_KEY||t.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||t.url||"https://cedar-api.fly.dev";return e?{url:n,ingestKey:e}:null}async function un(){let t=(await cn()).slack||{},e=process.env.BOSTRAT_SLACK_BOT_TOKEN||t.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||t.appToken;return!e||!n?null:{botToken:e,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||t.channel||null,maxConcurrent:Number.isInteger(t.maxConcurrent)&&t.maxConcurrent>0?t.maxConcurrent:2}}import Lr from"node:fs/promises";import om from"node:path";import fS from"node:os";import{randomUUID as sm}from"node:crypto";var Jp=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function F0(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function zp(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function ls(t,{boxLabels:e={}}={}){let n=(t||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=new Set,u=Jp();for(let d of n){let f=d.payload?.model||"unknown",w=d.provider||"unknown",m=d.boxId||"unattributed",g=F0(d.payload?.tokens),x=Number(d.payload?.usd)||0;!x&&g&&(x=Kt(f,g),x>0&&i++);let A=g?g.input+g.output+g.cacheRead+g.cacheWrite:0;if(!x&&A>0&&!Do(f)&&(c++,l.add(f)),a+=x,zp(u,g),r[f]||={usd:0,tokens:Jp(),events:0},r[f].usd+=x,r[f].events+=1,zp(r[f].tokens,g),o[w]||={usd:0,events:0},o[w].usd+=x,o[w].events+=1,s[m]||={usd:0,events:0,provider:w,models:new Set,label:e[m]||m},s[m].usd+=x,s[m].events+=1,f!=="unknown"&&s[m].models.add(f),d.epoch!=null){let S=(s[m].epochs||={})[d.epoch]||={usd:0,events:0,provider:w,models:new Set};S.usd+=x,S.events+=1,S.provider=w,f!=="unknown"&&S.models.add(f)}}return{totalUsd:Dr(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:cs(r,d=>({...d,usd:Dr(d.usd)})),byProvider:cs(o,d=>({...d,usd:Dr(d.usd)})),byBox:cs(s,d=>({usd:Dr(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:cs(d.epochs,f=>({usd:Dr(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var Dr=t=>Math.round(t*1e4)/1e4,cs=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));var rc={};zc(rc,{createComment:()=>X0,findIssueUrl:()=>G0,issueStates:()=>Q0,mdToAdf:()=>Zp,moveIssue:()=>tS,parseIssueRef:()=>q0,resolveIssue:()=>K0,uploadImage:()=>Y0,upsertCard:()=>V0,whoami:()=>nc});var zn=t=>String(t.baseUrl||"").replace(/\/$/,""),Yp=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function dn(t,e,n,r){let o=await fetch(`${zn(t)}${n}`,{method:e,headers:{authorization:Yp(t),accept:"application/json",...r!==void 0?{"content-type":"application/json"}:{}},body:r!==void 0?JSON.stringify(r):void 0}),s=await o.text(),a=null;try{a=s?JSON.parse(s):null}catch{a=null}if(!o.ok){let i=a?.errorMessages?.[0]??Object.values(a?.errors??{})[0]??`HTTP ${o.status}`,c=new Error(`jira: ${i}`);throw c.status=o.status,c}return a}var H0=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,W0=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function q0(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(H0);return r?r[1].toUpperCase():null}let n=e.match(W0);return n?n[1]:null}function G0(t,e=null){let n=null;try{n=e?.baseUrl?new URL(e.baseUrl).host:null}catch{n=null}let r=/https?:\/\/[^\s)]+\/browse\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/g;for(let o of String(t||"").matchAll(r))try{let s=new URL(o[0]).host;if(s===n||/\.atlassian\.net$/i.test(s))return o[0]}catch{}return null}async function K0(t,e){let n;try{n=await dn(t,"GET",`/rest/api/3/issue/${encodeURIComponent(e)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${e}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${zn(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function V0(t,e,{url:n,title:r,subtitle:o}){await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var J0=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function z0(t){let e=[],n=0;for(let r of t.matchAll(J0)){r.index>n&&e.push({type:"text",text:t.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);e.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?e.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):e.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<t.length&&e.push({type:"text",text:t.slice(n)}),e}function Zp(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
232
- `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...z0(o))}),{type:"paragraph",content:r.length?r:[{type:"text",text:" "}]}});return{type:"doc",version:1,content:e.length?e:[{type:"paragraph",content:[{type:"text",text:" "}]}]}}async function X0(t,e,n){let r=await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Zp(n)});return r?.id?`${zn(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function nc(t){let e=await dn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(zn(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function Y0(t,{issue:e,filename:n,contentType:r,buffer:o}){let s=new FormData;s.append("file",new Blob([o],{type:r}),n);let a=await fetch(`${zn(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:Yp(t),"x-atlassian-token":"no-check",accept:"application/json"},body:s}),i=await a.json().catch(()=>null),c=Array.isArray(i)?i[0]:null;if(!a.ok||!c)throw new Error(`jira: attachment upload failed (HTTP ${a.status})`);return c.content??`${zn(t)}/rest/api/3/attachment/content/${c.id}`}var Z0={new:"unstarted",indeterminate:"started",done:"completed"},Xp=t=>Z0[t?.key]??"started";async function Q0(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([dn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),dn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Xp(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Xp(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function tS(t,e,n){await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var ic={};zc(ic,{createComment:()=>cS,createIssue:()=>ac,createProject:()=>sc,findIssueUrl:()=>sS,issueStates:()=>uS,moveIssue:()=>dS,parseIssueRef:()=>oS,resolveIssue:()=>aS,teams:()=>Xn,uploadImage:()=>lS,upsertCard:()=>iS,whoami:()=>oc});var eS=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Qt(t,e,n={}){let r=await fetch(eS(),{method:"POST",headers:{"content-type":"application/json",authorization:t.apiKey},body:JSON.stringify({query:e,variables:n})}),o=await r.json().catch(()=>null);if(!r.ok)throw new Error(`linear: HTTP ${r.status}${o?.errors?.[0]?.message?` \u2014 ${o.errors[0].message}`:""}`);if(o?.errors?.length)throw new Error(`linear: ${o.errors[0].message}`);return o?.data??{}}var nS=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,rS=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function oS(t){let e=String(t||"").trim(),n=e.match(nS);if(n)return n[1].toUpperCase();let r=e.match(rS);return r?r[1]:null}function sS(t){let e=String(t||"").match(/https?:\/\/linear\.app\/[^/\s]+\/issue\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/);return e?e[0]:null}async function aS(t,e){let r=(await Qt(t,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:e})).issue;if(!r)throw new Error(`linear: issue "${e}" not found`);return{issueId:r.id,key:r.identifier,title:r.title,url:r.url,state:r.state?.name??null,team:r.team?.key??null}}async function iS(t,e,{url:n,title:r,subtitle:o}){let s=await Qt(t,"mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }",{input:{issueId:e.issueId,url:n,title:r,subtitle:o}});if(!s.attachmentCreate?.success)throw new Error("linear: attachmentCreate did not succeed");return s.attachmentCreate.attachment?.id??null}async function cS(t,e,n){let r=await Qt(t,"mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } }",{input:{issueId:e.issueId,body:n}});if(!r.commentCreate?.success)throw new Error("linear: commentCreate did not succeed");return r.commentCreate.comment?.url??null}async function oc(t){let e=await Qt(t,"query { viewer { name email } organization { name urlKey } }");return{user:e.viewer?.name??e.viewer?.email??null,org:e.organization?.name??e.organization?.urlKey??null}}async function lS(t,{filename:e,contentType:n,buffer:r}){let o=await Qt(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
231
+ `;await P.writeFile(U.join(t.dir,Zo),o)}async function P0(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]||{},...Yi(t)},await K())}async function N0(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 B0(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]||{},...Yi(t)},await K()}async function D0(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 tc(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 j0(t,e){let n=(e.startedAt||Date.now())+A0;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 ec(_0)}}function ec(t){return new Promise(e=>setTimeout(e,t))}import Hr from"node:fs/promises";import gs from"node:path";import ES from"node:os";import L0 from"node:fs/promises";import qp from"node:path";import U0 from"node:os";var Gp=()=>qp.join(process.env.BOSTRAT_STATE_DIR||qp.join(process.env.HOME||U0.tmpdir(),".bostrat"),"integrations.json");async function cn(){try{return JSON.parse(await L0.readFile(Gp(),"utf8"))||{}}catch{return{}}}var as=["linked","prOpened","prMerged","create"],nc=["prompt","auto","off"],F0={create:["prompt","off"]},Kp=t=>F0[t]??nc;function is(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of as){let s=Kp(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:is(t),defaultTeam:n}:null}async function cs(){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:is(t)}}async function rc(){return is(await cn())}async function Vp(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${as.join(", ")}`);for(let[r,o]of e){if(!as.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${as.join(", ")}`);let s=Kp(r);if(!s.includes(o))throw new Error(s.length<nc.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${nc.join(", ")}`)}let n=await cn();return n.ticketFlow={...is(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await qt(Gp(),n,{mode:384}),n.ticketFlow}async function Dr(){let t=(await cn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function Jp(){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 Ur from"node:fs/promises";import sm from"node:path";import pS from"node:os";import{randomUUID as am}from"node:crypto";var zp=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function H0(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function Xp(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function us(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=zp();for(let d of n){let f=d.payload?.model||"unknown",w=d.provider||"unknown",m=d.boxId||"unattributed",g=H0(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&&!jo(f)&&(c++,l.add(f)),a+=x,Xp(u,g),r[f]||={usd:0,tokens:zp(),events:0},r[f].usd+=x,r[f].events+=1,Xp(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:jr(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:ls(r,d=>({...d,usd:jr(d.usd)})),byProvider:ls(o,d=>({...d,usd:jr(d.usd)})),byBox:ls(s,d=>({usd:jr(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:ls(d.epochs,f=>({usd:jr(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var jr=t=>Math.round(t*1e4)/1e4,ls=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));var sc={};Yc(sc,{createComment:()=>Y0,findIssueUrl:()=>K0,issueStates:()=>tS,mdToAdf:()=>Qp,moveIssue:()=>eS,parseIssueRef:()=>G0,resolveIssue:()=>V0,uploadImage:()=>Z0,upsertCard:()=>J0,whoami:()=>oc});var zn=t=>String(t.baseUrl||"").replace(/\/$/,""),Zp=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:Zp(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 W0=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,q0=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function G0(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(W0);return r?r[1].toUpperCase():null}let n=e.match(q0);return n?n[1]:null}function K0(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 V0(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 J0(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 z0=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function X0(t){let e=[],n=0;for(let r of t.matchAll(z0)){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 Qp(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(...X0(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 Y0(t,e,n){let r=await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Qp(n)});return r?.id?`${zn(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function oc(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 Z0(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:Zp(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 Q0={new:"unstarted",indeterminate:"started",done:"completed"},Yp=t=>Q0[t?.key]??"started";async function tS(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:Yp(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Yp(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function eS(t,e,n){await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var lc={};Yc(lc,{createComment:()=>lS,createIssue:()=>cc,createProject:()=>ic,findIssueUrl:()=>aS,issueStates:()=>dS,moveIssue:()=>fS,parseIssueRef:()=>sS,resolveIssue:()=>iS,teams:()=>Xn,uploadImage:()=>uS,upsertCard:()=>cS,whoami:()=>ac});var nS=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Qt(t,e,n={}){let r=await fetch(nS(),{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 rS=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,oS=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function sS(t){let e=String(t||"").trim(),n=e.match(rS);if(n)return n[1].toUpperCase();let r=e.match(oS);return r?r[1]:null}function aS(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 iS(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 cS(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 lS(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 ac(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 uS(t,{filename:e,contentType:n,buffer:r}){let o=await Qt(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
233
233
  fileUpload(contentType: $contentType, filename: $filename, size: $size) {
234
234
  success
235
235
  uploadFile { uploadUrl assetUrl headers { key value } }
236
236
  }
237
- }`,{contentType:n,filename:e,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function uS(t,e){let n=await Qt(t,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:e.key});if(!n.issue)throw new Error(`linear: issue "${e.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function dS(t,e,n){if(!(await Qt(t,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:e.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Xn(t){return((await Qt(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}async function sc(t,{teamId:e,name:n,description:r}){let o=await Qt(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],name:n,...r?{description:r}:{}}}),s=o.projectCreate?.project;if(!o.projectCreate?.success||!s)throw new Error("linear: projectCreate did not succeed");return{id:s.id,url:s.url,name:s.name}}async function ac(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Qt(t,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:e,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var am={linear:{api:ic,config:ln},jira:{api:rc,config:is}};async function pc(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(am)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function fn(t){let e=am[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function mc(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var im=()=>process.env.BOSTRAT_STATE_DIR||om.join(process.env.HOME||fS.tmpdir(),".bostrat"),dc=()=>om.join(im(),"tickets.json"),cm=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),ds=t=>`${cm()}/track/${t}`,us=null,cc=null,lc=Promise.resolve();async function fs(){return us||(cc??=Lr.readFile(dc(),"utf8").then(t=>JSON.parse(t)?.tickets??{}).catch(()=>({})).then(t=>us=t).finally(()=>cc=null),cc)}async function Qp(){let t=JSON.stringify({tickets:us},null,2),e=`${dc()}.${process.pid}.tmp`;await Lr.mkdir(im(),{recursive:!0}),await Lr.writeFile(e,t),await Lr.rename(e,dc())}async function Oe(){lc=lc.then(Qp,Qp),await lc.catch(()=>{})}async function te(t){return(await fs())[t]??null}async function lm(){return{...await fs()}}async function um(){return(await pc()).length>0}async function Yn(t,e,{source:n="api"}={}){let r=await pc();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(e);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null},pr:null};(await fs())[t.id]=c,await Oe(),await R({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Ur(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await jr(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await pm(t.id,c,"linked"),c}async function hc(t){let e=await fs();return e[t]?(delete e[t],await Oe(),!0):!1}var fc=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function dm(t,e){if(!t.track?.cwd)return null;let n=ls(await B({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function pS(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${fc(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${fc(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function mS(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(fc(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Ur(t,e){let n=await fn(e);if(!n)return;let r=await dm(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:ds(t),title:`Bostrat Track: ${e.track.name}`,subtitle:mS(e,r)})}function ps(t){let e=new URL(cm());return`${e.protocol}//${t}.${e.host}/`}var hS={linked:"linked",opened:"prOpened",merged:"prMerged"};function wS(t,e,n){let r=o=>e.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(t?.type))return r("started")[0]??null}else if(n==="opened"){if(t?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&t?.type!=="completed")return r("completed")[0]??null;return null}async function fm(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Oe(),await R({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function pm(t,e,n){let r=await fn(e);if(!r)return;let o=r.cfg.ticketFlow?.[hS[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=mc(s,a),c=wS(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??e.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,e,c.id),await fm(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await R({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:sm(),provider:e.provider,key:e.key,url:e.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...e.pr?.number?{prNumber:e.pr.number}:{}}}).catch(()=>{})}async function mm(t){let e=await te(t);if(!e)throw new Error("no ticket linked to this Track");let n=await fn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=mc(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await Oe()),{key:e.key,url:e.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function wc(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await te(t);if(!r)throw new Error("no ticket linked to this Track");let o=await fn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=mc(s,a),c=a.find(u=>u.id===e);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Oe()),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await fm(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function hm(t,e){let n=await te(t);if(!n)throw new Error("no ticket linked to this Track");let r=await fn(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await R({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function wm(t,e,n){if(!e)throw new Error("offerId required");let r=await B({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await wc(t.id,o.payload.to?.id,{mode:"offer"})),await R({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var tm=50;async function gm(){let t=await ln();if(!t)throw new Error("no ticketing integration configured");return Xn(t)}function gS(t){let e=t&&typeof t=="object"?t:{},n=(Array.isArray(e.issues)?e.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=e.epic&&String(e.epic.name||"").trim()?{name:String(e.epic.name).trim(),description:e.epic.description?String(e.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>tm)throw new Error(`too many issues (${n.length}); cap is ${tm} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function yS(t,e,n,r){let o=r??await Xn(t),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(e.team){let i=s(e.team);if(i)return i}let a=await te(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(t.defaultTeam){let i=s(t.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function ym(t,e){let n=await ln();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=gS(e),o=await Xn(n),s=await yS(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=sm();return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function bm(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await B({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await ln();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Xn(a)).find(g=>g.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let m=await sc(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let g=await ac(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:g.key,title:g.title,url:g.url})}catch(g){u.push({title:m.title,message:g?.message||String(g)})}await R({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let f=l.filter(m=>m.kind==="issue"),w=null;return i.linkCurrentTrack&&f.length===1&&!await te(t.id)&&(w=await Yn(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:w?w.key:null}}async function jr(t,e){let n=await fn(t);n&&await n.api.createComment(n.cfg,t,e)}async function bS(t,e,n){let r=await fn(t);if(!r)return null;let o=await Ae(e);if(!o)return null;let s=rn(o,n);if(!s)return null;let a=await Lr.readFile(Bt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:t.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var em=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),uc=new Map;function kS(t,e){if(em()<=0)return Ur(t,e);if(uc.has(t))return;let n=setTimeout(()=>{uc.delete(t),te(t).then(r=>r?Ur(t,r):null).catch(()=>{})},em());n.unref?.(),uc.set(t,n)}var nm=new Map;async function rm(t,e,n){let r=nm.get(t)??0;Date.now()-r<600*1e3||(nm.set(t,Date.now()),await R({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function xS(t,{resolveTrack:e=null}={}){let n=await te(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await pc())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=e?await e(t.trackId):null;if(!s)return;try{await Yn(s,o,{source:"chat"})}catch(a){await rm(t.trackId,s.cwd,a)}return}if(!n.track.cwd&&e){let o=await e(t.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Oe())}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Oe(),await Ur(t.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Oe(),await Ur(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await dm(n,t.trackId).catch(()=>null):null;await jr(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
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 dS(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 fS(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 ic(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 cc(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 im={linear:{api:lc,config:ln},jira:{api:sc,config:cs}};async function hc(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(im)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function fn(t){let e=im[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function wc(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 cm=()=>process.env.BOSTRAT_STATE_DIR||sm.join(process.env.HOME||pS.tmpdir(),".bostrat"),pc=()=>sm.join(cm(),"tickets.json"),lm=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),fs=t=>`${lm()}/track/${t}`,ds=null,uc=null,dc=Promise.resolve();async function ps(){return ds||(uc??=Ur.readFile(pc(),"utf8").then(t=>JSON.parse(t)?.tickets??{}).catch(()=>({})).then(t=>ds=t).finally(()=>uc=null),uc)}async function tm(){let t=JSON.stringify({tickets:ds},null,2),e=`${pc()}.${process.pid}.tmp`;await Ur.mkdir(cm(),{recursive:!0}),await Ur.writeFile(e,t),await Ur.rename(e,pc())}async function Oe(){dc=dc.then(tm,tm),await dc.catch(()=>{})}async function te(t){return(await ps())[t]??null}async function um(){return{...await ps()}}async function dm(){return(await hc()).length>0}async function Yn(t,e,{source:n="api"}={}){let r=await hc();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 ps())[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 Fr(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await Lr(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await mm(t.id,c,"linked"),c}async function gc(t){let e=await ps();return e[t]?(delete e[t],await Oe(),!0):!1}var mc=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function fm(t,e){if(!t.track?.cwd)return null;let n=us(await B({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function mS(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${mc(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${mc(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function hS(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(mc(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Fr(t,e){let n=await fn(e);if(!n)return;let r=await fm(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:fs(t),title:`Bostrat Track: ${e.track.name}`,subtitle:hS(e,r)})}function ms(t){let e=new URL(lm());return`${e.protocol}//${t}.${e.host}/`}var wS={linked:"linked",opened:"prOpened",merged:"prMerged"};function gS(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 pm(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 mm(t,e,n){let r=await fn(e);if(!r)return;let o=r.cfg.ticketFlow?.[wS[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=wc(s,a),c=gS(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 pm(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:am(),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 hm(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=wc(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 yc(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=wc(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 pm(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function wm(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 gm(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 yc(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 em=50;async function ym(){let t=await ln();if(!t)throw new Error("no ticketing integration configured");return Xn(t)}function yS(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>em)throw new Error(`too many issues (${n.length}); cap is ${em} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function bS(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 bm(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=yS(e),o=await Xn(n),s=await bS(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=am();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 km(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 ic(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 cc(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 Lr(t,e){let n=await fn(t);n&&await n.api.createComment(n.cfg,t,e)}async function kS(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 Ur.readFile(Dt(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 nm=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),fc=new Map;function xS(t,e){if(nm()<=0)return Fr(t,e);if(fc.has(t))return;let n=setTimeout(()=>{fc.delete(t),te(t).then(r=>r?Fr(t,r):null).catch(()=>{})},nm());n.unref?.(),fc.set(t,n)}var rm=new Map;async function om(t,e,n){let r=rm.get(t)??0;Date.now()-r<600*1e3||(rm.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 SS(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 hc())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 om(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 Fr(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 Fr(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await fm(n,t.trackId).catch(()=>null):null;await Lr(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
238
238
 
239
- Agent cost for this Track: ${pS(o)}.`:"")),await pm(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await bS(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await jr(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
239
+ Agent cost for this Track: ${mS(o)}.`:"")),await mm(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await kS(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Lr(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
240
240
 
241
- ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await jr(n,`**Bostrat** \u2014 design decision: **${r.name}** picked${o?` over ${o}`:""}.`)}break}case"view.state.changed":{if(r.change!=="created"||r.restarted||!r.slug)break;await jr(n,`**Bostrat** \u2014 live preview **${r.name}**: ${ps(r.slug)}
242
- _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await kS(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await rm(t.trackId,s?.cwd??null,o)}}function km({resolveTrack:t}){Pt.on("event",e=>{xS(e,{resolveTrack:t}).catch(()=>{})})}import SS from"ws";var xm=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function ms(t,e,n={}){let r=await fetch(`${xm()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(n)}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}var gc=(t,e)=>ms("chat.postMessage",t,e),Zn=(t,e)=>ms("chat.update",t,e);async function Et(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function vS(t,e,n={}){let r=await fetch(`${xm()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/x-www-form-urlencoded; charset=utf-8"},body:new URLSearchParams(n).toString()}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}async function Sm(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await vS("files.getUploadURLExternal",t,{filename:e,length:String(n.length)}),l=await fetch(i,{method:"POST",body:n});if(!l.ok)throw new Error(`slack upload: http ${l.status}`);return ms("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var TS=2200;function vm({getToken:t,onEnvelope:e,log:n=()=>{}}){let r=!1,o=null,s=null,a=1e3;async function i(){if(r)return;let l;try{let u=await t();if(!u)throw new Error("app token missing");({url:l}=await ms("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new SS(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let f=g=>{try{o.send(JSON.stringify(g?{envelope_id:d.envelope_id,payload:g}:{envelope_id:d.envelope_id}))}catch{}},w=!1,m=setTimeout(()=>{w=!0,f()},TS);Promise.resolve().then(()=>e(d)).then(g=>{clearTimeout(m),!w&&(w=!0,f(d.accepts_response_payload?g:void 0))},g=>{clearTimeout(m),w||(w=!0,f()),n(`envelope handler failed: ${g?.message||g}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var _m=()=>process.env.BOSTRAT_STATE_DIR||ws.join(process.env.HOME||_S.tmpdir(),".bostrat"),Sc=()=>ws.join(_m(),"slack.json"),hs=null,yc=null,bc=Promise.resolve();async function Tc(){return hs||(yc??=Fr.readFile(Sc(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>hs=t).finally(()=>yc=null),yc)}async function Tm(){let t=JSON.stringify({threads:hs},null,2),e=`${Sc()}.${process.pid}.tmp`;await Fr.mkdir(_m(),{recursive:!0}),await Fr.writeFile(e,t),await Fr.rename(e,Sc())}async function ue(){bc=bc.then(Tm,Tm),await bc.catch(()=>{})}async function Ac(t){return(await Tc())[t]??null}async function ES(t,e){let n=await Tc();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var F=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function Hr(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function IS(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Hr(n.replace(/\n+/g," "),e)}function Em(t){let e=`*${F(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${F(t.repoLabel)} \xB7 \`${F(t.branch||"?")}\` \xB7 lead *${F(t.lead)}*`,r=t.kickoff?`> ${F(Hr(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
243
- ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${ds(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function vc(t,e){await Zn(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:Em(e)})}async function mn(t,e,n,r=void 0){return gc(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var Am=new Set;function pn(t){Am.add(t),t.catch(()=>{}).finally(()=>Am.delete(t))}var tt=t=>({response_type:"ephemeral",text:t}),RS="*/bostrat* \u2014 `start <name> [repo]` \xB7 `archive <track>` \xB7 `handoff <track> <agent>` \xB7 `stop <track>` \xB7 `list`\n_Reply in a Track's thread to steer its lead; question prompts and ship offers appear there as buttons._",$S=["claude","cursor","codex"];function kc(t,e){let n=(e||"").toLowerCase(),r=t.filter(s=>!s.archivedAt),o=r.filter(s=>s.name.toLowerCase().startsWith(n));return o.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),{match:o[0]??null,live:r}}var xc=(t,e)=>tt(`No live Track matches \u201C${F(e)}\u201D. Live: ${t.map(n=>`\`${F(n.name)}\``).join(" \xB7 ")||"none"}`);async function Im(t,e,{name:n,repo:r,responseUrl:o}){let s=t.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=e.maxConcurrent){await Et(o,{...tt(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Et(o,{...tt("No designated channel configured \u2014 set `slack.channel` in integrations.json."),replace_original:!0});return}let i=await s.createTrack({name:n,...r?{repo:r}:{}}),c=i.workspace||{},l={trackId:i.id,channel:e.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?ws.basename(c.base):"fresh workspace"),lead:"claude"},u=await gc(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:Em(l),unfurl_links:!1});l.ts=u.ts,(await Tc())[i.id]=l,await ue(),await Et(o,{...tt(`\u2713 *${F(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Et(o,{...tt(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function OS(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Ac(n.id);s&&(s.archivedAt=Date.now(),await ue(),await vc(e,s).catch(()=>{})),await Et(r,{...tt(`\u2713 *${F(n.name)}* archived.`),replace_original:!0})}catch(s){await Et(r,{...tt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function CS(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Et(o,{...tt(`\u2713 Handing *${F(n.name)}* to ${F(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Et(o,{...tt(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function MS(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await Et(r,{...tt(`\u2713 Stopped *${F(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await Et(r,{...tt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function PS(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Hr(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:t}),text:{type:"mrkdwn",text:`Repo for *${F(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function NS(t){let e=await t.listTracks(),n=e.filter(s=>!s.archivedAt);if(n.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),!n.length)return"No live Tracks.";let r=n.map(s=>{let a=s.workspace||{},i=a.repo||(a.base?ws.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${F(s.name)}* \u2014 ${F(i)}${a.branch?` \`${F(a.branch)}\``:""} \xB7 lead ${F(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
241
+ ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Lr(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 Lr(n,`**Bostrat** \u2014 live preview **${r.name}**: ${ms(r.slug)}
242
+ _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await xS(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await om(t.trackId,s?.cwd??null,o)}}function xm({resolveTrack:t}){Nt.on("event",e=>{SS(e,{resolveTrack:t}).catch(()=>{})})}import vS from"ws";var Sm=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function hs(t,e,n={}){let r=await fetch(`${Sm()}/${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 bc=(t,e)=>hs("chat.postMessage",t,e),Zn=(t,e)=>hs("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 TS(t,e,n={}){let r=await fetch(`${Sm()}/${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 vm(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await TS("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 hs("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var AS=2200;function Tm({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 hs("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new vS(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()},AS);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 Em=()=>process.env.BOSTRAT_STATE_DIR||gs.join(process.env.HOME||ES.tmpdir(),".bostrat"),Tc=()=>gs.join(Em(),"slack.json"),ws=null,kc=null,xc=Promise.resolve();async function _c(){return ws||(kc??=Hr.readFile(Tc(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>ws=t).finally(()=>kc=null),kc)}async function Am(){let t=JSON.stringify({threads:ws},null,2),e=`${Tc()}.${process.pid}.tmp`;await Hr.mkdir(Em(),{recursive:!0}),await Hr.writeFile(e,t),await Hr.rename(e,Tc())}async function le(){xc=xc.then(Am,Am),await xc.catch(()=>{})}async function Ec(t){return(await _c())[t]??null}async function IS(t,e){let n=await _c();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 Wr(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function RS(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Wr(n.replace(/\n+/g," "),e)}function Im(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(Wr(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:`<${fs(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Ac(t,e){await Zn(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:Im(e)})}async function mn(t,e,n,r=void 0){return bc(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var _m=new Set;function pn(t){_m.add(t),t.catch(()=>{}).finally(()=>_m.delete(t))}var et=t=>({response_type:"ephemeral",text:t}),$S="*/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._",OS=["claude","cursor","codex"];function Sc(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 vc=(t,e)=>et(`No live Track matches \u201C${F(e)}\u201D. Live: ${t.map(n=>`\`${F(n.name)}\``).join(" \xB7 ")||"none"}`);async function Rm(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,{...et(`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,{...et("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?gs.basename(c.base):"fresh workspace"),lead:"claude"},u=await bc(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:Im(l),unfurl_links:!1});l.ts=u.ts,(await _c())[i.id]=l,await le(),await Et(o,{...et(`\u2713 *${F(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Et(o,{...et(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function CS(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Ec(n.id);s&&(s.archivedAt=Date.now(),await le(),await Ac(e,s).catch(()=>{})),await Et(r,{...et(`\u2713 *${F(n.name)}* archived.`),replace_original:!0})}catch(s){await Et(r,{...et(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function MS(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Et(o,{...et(`\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,{...et(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function PS(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,{...et(`\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,{...et(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function NS(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Wr(`${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 BS(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?gs.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${F(s.name)}* \u2014 ${F(i)}${a.branch?` \`${F(a.branch)}\``:""} \xB7 lead ${F(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
244
244
  `)+(o?`
245
- _${o} archived_`:"")}async function BS(t,e){let n=await un();if(!n)return tt("Slack integration not configured.");let r=t.deps,o=(e.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return tt("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(pn(Im(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),tt(`Starting *${F(u)}* on ${F(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:PS(u,i)}}case"archive":{if(!a.length)return tt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=kc(await r.listTracks(),a.join(" "));return i?(pn(OS(t,n,{track:i,responseUrl:e.response_url})),tt(`Archiving *${F(i.name)}*\u2026`)):xc(c,a.join(" "))}case"handoff":{let i=r.knownAgents??$S,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return tt(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=kc(await r.listTracks(),l);return u?(pn(CS(t,n,{track:u,agent:c,responseUrl:e.response_url})),tt(`Handing *${F(u.name)}* to ${F(c)}\u2026`)):xc(d,l)}case"stop":{if(!a.length)return tt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=kc(await r.listTracks(),a.join(" "));return i?(pn(MS(t,n,{track:i,responseUrl:e.response_url})),tt(`Stopping *${F(i.name)}*\u2026`)):xc(c,a.join(" "))}case"list":return tt(await NS(r));default:return tt(RS)}}async function DS(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Ac(o);try{if(await t.deps.answerPrompt(o,{promptId:s,answers:[a]}),i?.prompt?.promptId===s){let{ts:c,question:l}=i.prompt;delete i.prompt,await ue(),await Zn(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(l||"question")}~
246
- \u2713 *${F(a)}*`}}]}).catch(()=>{})}}catch(c){await Et(n.response_url,tt(`Couldn't answer: ${c?.message||c}`))}}async function jS(t,e,n,r){let{trackId:o,offerId:s,action:a}=r,i=t.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(f=>f.type==="pr.offered"&&f.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===s))throw new Error("offer already answered");let d=a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;d&&await i.sayToLead(c.id,To(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await Et(n.response_url,tt(`Couldn't act on the offer: ${c?.message||c}`))}}async function LS(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await un();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await Et(e.response_url,{...tt(`Starting *${F(s)}*\u2026`),replace_original:!0}),pn(Im(t,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:e.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?pn(DS(t,r,e,o)):n.action_id==="bostrat-ship"&&pn(jS(t,r,e,o))}async function US(t,e){if(!e||e.type!=="message"||e.bot_id||e.subtype||!e.thread_ts||e.thread_ts===e.ts||!e.text?.trim())return;let n=await ES(e.channel,e.thread_ts);if(!n)return;let r=await un();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await mn(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function FS(t){return async e=>{if(e.type==="slash_commands")return BS(t,e.payload);if(e.type==="interactive"){await LS(t,e.payload);return}if(e.type==="events_api"){await US(t,e.payload?.event);return}}}var HS=new Set(["turn.user","turn.agent","prompt.raised","prompt.answered","pr.offered","pr.offer.answered","pr.state.changed","handoff.done","view.state.changed","mock.state.changed"]);function WS(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${F(e.question||"The lead needs an answer")}`}}],r=(e.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:Hr(String(o),75)},value:JSON.stringify({trackId:t,promptId:e.promptId,label:String(o)})}))}):n.push({type:"context",elements:[{type:"mrkdwn",text:"_reply in this thread to answer_"}]}),n}async function qS(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await Zn(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await mn(t,e,r.question||"The lead needs an answer",WS(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await ue()}async function GS(t,e,n){let r=n.payload||{};if(!e.prompt||r.promptId&&e.prompt.promptId!==r.promptId)return;let{ts:o,question:s}=e.prompt;delete e.prompt,await ue(),await Zn(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(s||"question")}~
247
- \u2713 _answered_`}}]})}async function KS(t,e,n){let r=n.payload||{};if(!r.offerId||e.offers?.[r.offerId])return;let o=(i,c,l)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i}),...l?{style:l}:{}}),s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")],a=await mn(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${F(Hr(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await ue()}async function VS(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await ue();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await Zn(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function JS(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??zS)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Sm(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${F(o.name)}*${a}${o.group?` (group \`${F(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${F(a.name)}*`).join(", ");await mn(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${F(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function zS(t,e){let n=await Ae(t);if(!n)return null;let r=rn(n,e);if(!r)return null;let o=await Fr.readFile(Bt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function XS(t,e){if(!HS.has(e.type))return;let n=await Ac(e.trackId);if(!n)return;let r=await un();if(!r)return;let o=e.payload||{};switch(e.type){case"turn.user":if(n.kickoff||e.actor!=="user"||!o.text)return;n.kickoff=o.text,await ue(),await vc(r,n);return;case"turn.agent":{if(!o.text)return;let s=IS(o.text);if(!s)return;await mn(r,n,s,[{type:"section",text:{type:"mrkdwn",text:F(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${ds(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return qS(r,n,e);case"prompt.answered":return GS(r,n,e);case"pr.offered":return KS(r,n,e);case"pr.offer.answered":return VS(r,n,e);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await mn(r,n,`${s}${o.url?`: ${o.url}`:o.number?` (#${o.number})`:""}`);return}case"handoff.done":if(!o.toAgent||o.toAgent===n.lead)return;n.lead=o.toAgent,await ue(),await vc(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await mn(r,n,`Live preview ${o.name}: ${ps(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${F(o.name)}*: ${ps(o.slug)}
248
- _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return JS(r,n,e,t.deps)}}function Rm(t){let e={deps:t};Pt.on("event",s=>{XS(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await un()||(r&&clearInterval(r),r=null,n=vm({getToken:async()=>(await un())?.appToken,onEnvelope:FS(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import YS from"node:fs/promises";function ZS(t){let e=new Map;for(let n of String(t||"").split(`
249
- `)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&e.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...e.values()]}function QS(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}function tv(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?ne(e.cwd,e.sessionId):e?.cwd?Ws(e.cwd):null}async function $m(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await R(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":await i({type:"turn.user",actor:"user",payload:{promptHead:String(t.prompt??t.user_prompt??"").slice(0,160)||null}});break;case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:{tool:t.tool_name??null}});break;case"Stop":await ev(r,o,t,i);break;default:break}return a}async function ev(t,e,n,r){let o=[],s=tv(n,e);if(s)try{o=ZS(await YS.readFile(s,"utf8"))}catch{}let a=o.length?o[o.length-1]:null;if(await r({type:"turn.agent",actor:"agent",payload:{model:a?.model??e?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=QS(await B(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=d.model||"unknown",w=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(w.tokens))w.tokens[m]+=d.tokens[m];w.messageIds.push(d.messageId),u.set(f,w)}for(let[d,f]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:d,usd:Kt(d,f.tokens),tokens:f.tokens,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}import ov from"node:fs";import _c from"node:fs/promises";import sv from"node:http";import Qn from"node:path";import ys from"node:os";import{createRequire as av}from"node:module";import Ec from"ws";import nv from"node:path";import rv from"node:os";var Y$=process.env.BOSTRAT_STATE_DIR||nv.join(process.env.HOME||rv.tmpdir(),".bostrat");function Om(t=process.env){let e=(t.BOSTRAT_HOST||"").trim();if(e!=="all")return e||(t.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function Cm(t){let e=String(t||"").trim();if(!e)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`wss://${e}`,r;try{r=new URL(n)}catch{return e}let o={"https:":"wss:","http:":"ws:","wss:":"wss:","ws:":"ws:"}[r.protocol];return o?(r.protocol=o,(r.pathname===""||r.pathname==="/")&&(r.pathname="/v1/node"),r.toString()):e}var gs=(()=>{try{return av(import.meta.url)("../package.json").version||null}catch{return null}})(),iv=process.env.BOSTRAT_STATE_DIR||Qn.join(process.env.HOME||ys.tmpdir(),".bostrat");function cv(t,e){let n=String(t).split(".").map(Number),r=String(e).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function lv({home:t=process.env.HOME||ys.homedir(),env:e=process.env}={}){let n=r=>{try{return ov.statSync(r),!0}catch{return!1}};return{claude:n(Qn.join(t,".claude",".credentials.json")),codex:n(Qn.join(e.CODEX_HOME||Qn.join(t,".codex"),"auth.json"))}}var St=1,Mm=1e3,uv=3e4,Pm=100,dv=2e3,fv=25e3;function Nm({url:t,token:e,nodeId:n=ys.hostname(),name:r=ys.hostname(),listTracks:o,selfUrl:s=`http://127.0.0.1:${process.env.PORT||3362}`,log:a=console}){if(!t||!e)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");t=Cm(t);let i=s.replace(/^http/,"ws"),c=new Map,l=new Map,u=Qn.join(iv,"plane-link","cursor.json"),d={},f=new Map,w=null,m=!1,g=!1,x=Mm,A=null,S=null,T=null,E=!1;async function J(){try{d=JSON.parse(await _c.readFile(u,"utf8"))}catch{d={}}}function M(){E=!0,!S&&(S=setTimeout(async()=>{if(S=null,!!E){E=!1;try{await _c.mkdir(Qn.dirname(u),{recursive:!0}),await _c.writeFile(u,JSON.stringify(d))}catch(N){a.error("[plane-link] cursor flush failed:",N?.message||N)}}},dv),S.unref?.())}function I(N){return!w||w.readyState!==Ec.OPEN?!1:(w.send(JSON.stringify(N)),!0)}function h(N){for(let{trackId:W,event:_}of N)f.set(_.id,{trackId:W,seq:_.seq});return I({v:St,ch:"events",type:"append",events:N})}async function p(){let N;try{N=await o()}catch(W){a.error("[plane-link] backfill listTracks failed:",W?.message||W);return}I({v:St,ch:"ctl",type:"tracks",ids:N.map(W=>W.id).filter(W=>typeof W=="string")});for(let W of N){if(!m)return;let _;try{_=await B(W)}catch{continue}let q=d[W.id]??0,X=_.filter(lt=>lt.seq>q&&typeof lt.id=="string");for(let lt=0;lt<X.length;lt+=Pm){let et=X.slice(lt,lt+Pm).map(wt=>({trackId:W.id,event:wt}));if(!h(et))return}}}function b(N){!m||!N?.trackId||typeof N.id!="string"||h([{trackId:N.trackId,event:N}])}let v=new Map;async function ot(N){let{reqId:W,method:_,path:q,headers:X={},body:lt}=N,et=Object.fromEntries(Object.entries(X).filter(([,Mt])=>Mt!=null)),wt=new AbortController;v.set(W,wt);try{if(N.host){await $(N,et,wt);return}let Mt=await fetch(s+q,{method:_,headers:et,body:lt?Buffer.from(lt,"base64"):void 0,signal:wt.signal});if(I({v:St,ch:"control",type:"http_head",reqId:W,status:Mt.status,headers:{"content-type":Mt.headers.get("content-type")||"application/octet-stream"}}),Mt.body){for await(let De of Mt.body)if(!I({v:St,ch:"control",type:"http_data",reqId:W,data:Buffer.from(De).toString("base64")}))break}I({v:St,ch:"control",type:"http_end",reqId:W,error:null})}finally{v.delete(W)}}function $(N,W,_){let{reqId:q,method:X,path:lt,body:et}=N,wt=new URL(s);return new Promise(Mt=>{let De=sv.request({host:wt.hostname,port:wt.port,method:X,path:lt,headers:{...W,host:N.host},signal:_.signal},Dt=>{let{connection:y,"keep-alive":j,"transfer-encoding":wn,upgrade:Yr,...je}=Dt.headers;I({v:St,ch:"control",type:"http_head",reqId:q,status:Dt.statusCode||502,headers:je}),Dt.on("data",$s=>{I({v:St,ch:"control",type:"http_data",reqId:q,data:Buffer.from($s).toString("base64")})||Dt.destroy()}),Dt.on("end",()=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:null}),Mt()}),Dt.on("error",()=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:"view upstream error"}),Mt()})});De.on("error",Dt=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:Dt?.message||"view request failed"}),Mt()}),et&&De.write(Buffer.from(et,"base64")),De.end()})}function z(){if(g)return;w=new Ec(t,{headers:{authorization:`Bearer ${e}`}}),w.on("open",()=>{I({v:St,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:gs,providers:lv()})}),w.on("message",W=>{let _;try{_=JSON.parse(W)}catch{return}if(_.ch==="ctl"&&_.type==="hello_ok"){m=!0,x=Mm,a.log(`[plane-link] connected to plane as ${n}`),_.minNodeVersion&&gs&&cv(gs,_.minNodeVersion)&&a.error(`[plane-link] this node is bostrat-node ${gs}; the plane asks for \u2265${_.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),T&&clearInterval(T),T=setInterval(()=>I({v:St,ch:"ctl",type:"ping"}),fv),T.unref?.(),p().catch(q=>a.error("[plane-link] backfill failed:",q?.message||q));return}if(_.ch==="events"&&_.type==="ack"&&Array.isArray(_.ids)){for(let q of _.ids){let X=f.get(q);X&&(f.delete(q),(d[X.trackId]??0)<X.seq&&(d[X.trackId]=X.seq))}M();return}if(_.ch==="control"&&_.type==="http_open"){ot(_).catch(q=>I({v:St,ch:"control",type:"http_end",reqId:_.reqId,error:q?.message||"tunnel error"}));return}if(_.ch==="control"&&_.type==="http_cancel"){v.get(_.reqId)?.abort();return}if(_.ch==="attach"&&_.type==="open"){let q=new Ec(i+_.path,Array.isArray(_.protocols)?_.protocols:[],_.host?{headers:{host:_.host}}:void 0);c.set(_.streamId,q),l.set(_.streamId,[]),q.on("open",()=>{let lt=l.get(_.streamId)||[];l.delete(_.streamId);for(let{buf:et,bin:wt}of lt)q.send(et,{binary:wt})}),q.on("message",(lt,et)=>{let wt=et?lt:Buffer.from(String(lt));I({v:St,ch:"attach",type:"data",streamId:_.streamId,data:wt.toString("base64"),...et?{bin:!0}:{}})});let X=()=>{l.delete(_.streamId),c.delete(_.streamId)&&I({v:St,ch:"attach",type:"close",streamId:_.streamId})};q.on("close",X),q.on("error",X);return}if(_.ch==="attach"&&_.type==="data"){let q=c.get(_.streamId);if(!q)return;let X=Buffer.from(_.data,"base64"),lt=_.bin===!0,et=l.get(_.streamId);et?et.push({buf:X,bin:lt}):q.send(X,{binary:lt});return}if(_.ch==="attach"&&_.type==="close"){let q=c.get(_.streamId);c.delete(_.streamId),l.delete(_.streamId),q?.close();return}});let N=()=>{m&&a.log("[plane-link] disconnected from plane"),m=!1,T&&(clearInterval(T),T=null),f.clear();for(let W of c.values())W.close();c.clear(),l.clear();for(let W of v.values())W.abort();v.clear(),!(g||A)&&(A=setTimeout(()=>{A=null,z()},x),A.unref?.(),x=Math.min(x*2,uv))};w.on("close",N),w.on("error",N)}return Pt.on("event",b),J().then(z),{stop:()=>{g=!0,Pt.off("event",b),A&&clearTimeout(A),S&&clearTimeout(S),T&&clearInterval(T),w?.close()},status:()=>({connected:m,nodeId:n,cursor:{...d},inflight:f.size})}}function pv(t){let e={};for(let n of t.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";e[n.name]=`${n.name} (${n.agent||"?"}${r})`}return e}var Bm=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Dm=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Ic=t=>Math.round(t*1e4)/1e4;async function jm(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=Bm(),u=0,d=[];for(let f of t||[]){let w=await B(f),m=ls(w,{boxLabels:pv(f)});if(m.turns===0){f.archivedAt?u++:d.push(f.name||f.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.unpricedTurns||0;for(let g of m.unpricedModels||[])c.add(g);Dm(l,m.totalTokens);for(let[g,x]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:Bm()},n[g].usd=Ic(n[g].usd+x.usd),n[g].events+=x.events,Dm(n[g].tokens,x.tokens);for(let[g,x]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=Ic(r[g].usd+x.usd),r[g].events+=x.events;e.push({id:f.id,name:f.name,usd:m.totalUsd,rollup:m})}return e.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Ic(o),turns:s,repricedTurns:a,unpricedTurns:i,unpricedModels:[...c],totalTokens:l,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:u+d.length,tracksSilentArchived:u,tracksSilentLive:d},tracks:e}}var Wr=t=>`$${t.toFixed(4)}`,tr=(t,e)=>String(t).padEnd(e);function Lm(t){let{fleet:e,tracks:n}=t,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${t.generatedAt}`),t.pricingBasis&&r.push(`basis: ${t.pricingBasis}`);let o=e.tracksSilentLive||[];r.push(`fleet total ${Wr(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) imputed at current rates (recorded before their model was priced)`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${tr(a,26)} ${tr(Wr(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${tr(a.name||a.id,30)} ${Wr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${tr(c.label,40)} ${tr(Wr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${tr(Wr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
250
- `)}import Sv from"node:fs/promises";import vv from"node:path";var mv=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],hv={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope out\b/,/\btalk through\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/]};function bs(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of mv){let o=0;for(let s of hv[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var wv=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),gv=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Um(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))wv.has(r)&&(e+=o||0),gv.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Rc(t){return String(t||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var ks=t=>t.payload?.ts??t.ts,yv=12e4,Ce=t=>Math.round(t*1e4)/1e4,bv=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function kv(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>ks(S)>=e,a=bs([Rc(n?.name),Rc(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let T=bs(S.payload.task);T&&i.set(S.boxId,T)}let c=new Map,l=S=>{let T=S||"unattributed";return c.has(T)||c.set(T,{segments:[u(null)],lastTs:null,provider:null}),c.get(T)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((S,T)=>ks(S)-ks(T)),f=0,w=0,m=0,g={turns:0};for(let S of d){let T=l(S.boxId),E=T.segments[T.segments.length-1],J=ks(S);switch(T.lastTs!=null&&J>T.lastTs&&s(S)&&(E.activeMs+=Math.min(J-T.lastTs,yv)),T.lastTs=J,S.provider&&(T.provider=S.provider),S.type){case"turn.user":{let M=S.payload?.text;if(S.source==="hook"||!String(M||"").trim())break;let I=u(bs(M));T.segments.push(I),s(S)&&(I.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let M=S.payload?.tool;M&&(E.tools[M]=(E.tools[M]||0)+1),s(S)&&(E.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let M=S.payload||{},I={input:M.tokens?.input||0,output:M.tokens?.output||0,cacheRead:M.tokens?.cacheRead||0,cacheWrite:M.tokens?.cacheWrite??M.tokens?.cacheCreation??0},h=Number(M.usd)||0;!h&&M.tokens&&(h=Kt(M.model,I)),!h&&M.tokens&&!Do(M.model)&&(g.turns+=1),E.usd+=h,E.tokens.input+=I.input,E.tokens.output+=I.output,E.tokens.cacheRead+=I.cacheRead,E.tokens.cacheWrite+=I.cacheWrite;let p=M.model||"unknown";E.models[p]=(E.models[p]||0)+h,S.provider&&(E.provider=S.provider);break}case"handoff.done":s(S)&&(f+=1);break;case"subtask.merged":s(S)&&(w+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let x=new Map,A=!1;for(let[S,T]of c)for(let E of T.segments){if(!(E.usd>0||E.activeMs>0||E.turns>0||E.toolCalls>0))continue;let M=E.textCat?.category??i.get(S)?.category??Um(E.tools)?.category??a?.category??"uncategorized";x.has(M)||x.set(M,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let I=x.get(M);I.usd+=E.usd,I.activeMs+=E.activeMs,I.turns+=E.turns,I.toolCalls+=E.toolCalls;for(let[p,b]of Object.entries(E.models))I.models[p]=(I.models[p]||0)+b;let h=E.provider||T.provider;h&&(I.providers[h]=(I.providers[h]||0)+E.usd),h==="cursor"&&E.usd===0&&(A=!0)}return{perCategory:x,handoffs:f,merged:w,failed:m,sawCursorActivity:A,unpricedTurns:g.turns}}function Hm(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let g of t||[]){let{track:x,events:A}=g;if(!A?.length){a+=1;continue}let{perCategory:S,handoffs:T,merged:E,failed:J,sawCursorActivity:M,unpricedTurns:I}=kv(g,{since:r});M&&(i=!0),c+=I;let h={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:T,subtasksMerged:E,subtasksFailed:J};for(let[p,b]of S){h.usd+=b.usd,h.activeMs+=b.activeMs,h.turns+=b.turns,h.categories[p]={usd:Ce(b.usd),activeMs:b.activeMs};for(let[$,z]of Object.entries(b.models))h.models[$]=Ce((h.models[$]||0)+z);for(let[$,z]of Object.entries(b.providers))h.providers[$]=Ce((h.providers[$]||0)+z);o.has(p)||o.set(p,bv());let v=o.get(p);v.usd+=b.usd,v.activeMs+=b.activeMs,v.turns+=b.turns,v.toolCalls+=b.toolCalls;for(let[$,z]of Object.entries(b.models))v.models[$]=(v.models[$]||0)+z;for(let[$,z]of Object.entries(b.providers))v.providers[$]=(v.providers[$]||0)+z;let ot=v.byTrack.get(x?.id)||{usd:0,activeMs:0};ot.usd+=b.usd,ot.activeMs+=b.activeMs,v.byTrack.set(x?.id,ot)}(h.usd>0||h.activeMs>0||h.turns>0)&&(h.usd=Ce(h.usd),h.dominantCategory=xv(h.categories),s.push(h))}let l=[...o.values()].reduce((g,x)=>g+x.usd,0),u=[...o.values()].reduce((g,x)=>g+x.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,w=new Map((t||[]).map(g=>[g.track?.id,g.track?.name||g.track?.id])),m=[...o.entries()].map(([g,x])=>({category:g,sharePct:f>0?Math.round((d==="usd"?x.usd:x.activeMs)/f*100):0,shareBasis:d,usd:Ce(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:Fm(x.models),providers:Fm(x.providers),topWorkstreams:[...x.byTrack.entries()].sort((A,S)=>S[1].usd-A[1].usd||S[1].activeMs-A[1].activeMs).slice(0,2).map(([A,S])=>({trackId:A,name:w.get(A),usd:Ce(S.usd),activeMs:S.activeMs}))})).sort((g,x)=>x.usd-g.usd||x.activeMs-g.activeMs);return s.sort((g,x)=>x.usd-g.usd||x.activeMs-g.activeMs),{since:r,capturedAt:n,totalUsd:Ce(l),activeMs:u,turns:[...o.values()].reduce((g,x)=>g+x.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var xv=t=>{let e=null;for(let[n,r]of Object.entries(t))(!e||r.usd>e.v.usd||r.usd===e.v.usd&&r.activeMs>e.v.activeMs)&&(e={cat:n,v:r});return e?.cat??null},Fm=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Ce(n)]));var $c={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function Tv(t){let e=String(t||"7d");return e in $c?{key:e,ms:$c[e]}:{key:"7d",ms:$c["7d"]}}function Av(t,e){let n=new Set((e||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...e||[]];for(let o of t?.events||[]){let s=vo(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:t.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function _v(t){if(!t?.cwd)return null;try{let n=(await Sv.readFile(vv.join(t.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function Wm(t,{window:e="7d",now:n=Date.now()}={}){let{key:r,ms:o}=Tv(e),s=[];for(let a of t||[]){let i=await B(a);s.push({track:a,events:Av(a,i),brief:await _v(a)})}return{window:r,...Hm(s,{windowMs:o,now:n})}}import er from"node:fs/promises";import Ev from"node:fs";import ft from"node:path";import Iv from"node:os";import{execFile as Rv}from"node:child_process";import{promisify as $v}from"node:util";var Ov=$v(Rv),qm=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||Iv.tmpdir(),".bostrat"),Oc=!1;async function qr(t,e){let n=await er.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await er.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await er.mkdir(ft.dirname(e),{recursive:!0}),await er.copyFile(t,e),!0)}var Cv=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Gr(t,e){let n=0,r=await er.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let o of r){if(Cv.has(o.name))continue;let s=ft.join(t,o.name);o.isDirectory()?n+=await Gr(s,ft.join(e,o.name)):o.isFile()&&(n+=await qr(s,ft.join(e,o.name))?1:0)}return n}async function Gm({stateDir:t=qm,dest:e=process.env.BOSTRAT_SYNC_DIR||`${qm}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await er.mkdir(e,{recursive:!0});let o=0;o+=await qr(ft.join(t,"tracks.json"),ft.join(e,"tracks.json"))?1:0,o+=await qr(ft.join(t,"mocks.json"),ft.join(e,"mocks.json"))?1:0,o+=await qr(ft.join(t,"runbooks.json"),ft.join(e,"runbooks.json"))?1:0,o+=await Gr(ft.join(t,"runbooks"),ft.join(e,"runbooks")),o+=await Gr(ft.join(t,"mock-archive"),ft.join(e,"mock-archive")),o+=await qr(ft.join(t,"events.jsonl"),ft.join(e,"events.jsonl"))?1:0,o+=await Gr(ft.join(t,"events"),ft.join(e,"events"));for(let l of r||[])!l?.cwd||!l?.id||(o+=await Gr(ft.join(l.cwd,".bostrat"),ft.join(e,"workspaces",l.id,".bostrat")));let s=(...l)=>Ov("git",["-C",e,...l]);Ev.existsSync(ft.join(e,".git"))||(await s("init","-q"),await s("config","user.email","agent@bostrat.local"),await s("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await s("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await la(e).catch(()=>{}));let a=!1,{stdout:i}=await s("status","--porcelain");i.trim()&&(await s("add","-A"),await s("commit","-q","-m",`state sync ${new Date().toISOString()}`),a=!0);let c=!1;if(n&&(a||Oc))try{await s("push","-q",n,"HEAD:refs/heads/main"),c=!0,Oc=!1}catch{Oc=!0}return{staged:o,committed:a,pushed:c}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as Mv}from"node:child_process";import{promisify as Pv}from"node:util";import{randomUUID as Nv}from"node:crypto";import Bv from"node:fs";import Dv from"node:os";import jv from"node:path";var Pe=Pv(Mv),xs=t=>new Promise(e=>setTimeout(e,t)),Ne=Ns,Cc=process.env.BOSTRAT_USAGE_PROBE_CWD||jv.join(Dv.homedir(),".bostrat","usage-probe"),Lv=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,Uv=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,Fv=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Km=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Me=null,Kr=null,Vr=null;async function Hv(){try{return await Pe(O,C("has-session","-t",Ne)),!0}catch{return!1}}function Wv(){Me&&clearTimeout(Me),Me=setTimeout(()=>qv().catch(()=>{}),Lv),Me.unref&&Me.unref()}async function qv(){Me&&(clearTimeout(Me),Me=null);try{await Pe(O,C("kill-session","-t",Ne))}catch{}}async function Gv(){let t=Date.now()+Fv;for(;;){let e=await Mc().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await xs(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await xs(500)}}async function Kv(){if(await Hv())return;if(!await ee())throw new Error("host tmux server not running \u2014 start globo-tmux.service");Bv.mkdirSync(Cc,{recursive:!0}),await pe(Cc);let t=Nv();await Pe(O,C("new-session","-d","-s",Ne,"-c",Cc,"-x","200","-y","60"));let e=`${Ps} --session-id ${t}`;await Pe(O,C("send-keys","-t",Ne,e,"Enter")),await Gv()}async function Mc(){let{stdout:t}=await Pe(O,C("capture-pane","-t",Ne,"-p"));return t}async function Vv(){await Kv(),await Pe(O,C("send-keys","-t",Ne,"-l","/usage")),await Pe(O,C("send-keys","-t",Ne,"Enter"));let t=Date.now()+Uv,e="";for(;await xs(400),e=await Mc(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await xs(900),e=await Mc(),await Pe(O,C("send-keys","-t",Ne,"Escape")).catch(()=>{}),Wv(),e}function Jv(t){let e=String(t||"").split(`
251
- `).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,e.length)&&!/^Current\s/i.test(e[c]);c++){let l=e[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=e[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=e.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return e.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function zv(t){let e=String(t||"").split(`
252
- `).map(s=>s.replace(/\s+$/,"")),n=e.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<e.length&&!r.test(e[s]);s++)e[s].trim()&&o.push(e[s].trim());return o.length?o:null}async function Vm({fresh:t=!1}={}){return!t&&Kr&&Date.now()-Kr.at<Km?Kr.data:Vr||(Vr=(async()=>{try{let e=await Vv(),n=Jv(e),r={...n,contributors:zv(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Kr={at:Date.now(),data:r},r}catch(e){let n={session:null,week:null,contributors:null,subscription:!1,error:e?.message||String(e),capturedAt:Date.now()};return Kr={at:Date.now()-(Km-3e3),data:n},n}finally{Vr=null}})(),Vr)}var Xv=262144;function Yv(t){let e=Number(t);if(!Number.isFinite(e)||e<0)return null;let n=Math.floor(e/3600),r=Math.round(e%3600/60);return n>=48?`in ${Math.round(n/24)}d`:n>0?`in ${n}h ${r}m`:`in ${Math.max(1,r)}m`}function Jm(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:Yv(t.resets_in_seconds)}:null}function Zv(t){let e=String(t||"").split(`
253
- `),n=!1;for(let r=e.length-1;r>=0;r--){let o=e[r].trim();if(!o||o[0]!=="{"||!o.includes("token_count"))continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s?.payload;if(a?.type!=="token_count")continue;n=!0;let i=a.rate_limits;if(!i)return{session:null,week:null,sawTokenCount:n};if(i.primary||i.secondary)return{session:Jm(i.primary),week:Jm(i.secondary),sawTokenCount:n};if(i.primary_used_percent!=null||i.secondary_used_percent!=null){let c=l=>Number.isFinite(Number(l))?{pct:Math.round(Number(l)),resets:null}:null;return{session:c(i.primary_used_percent),week:c(i.secondary_used_percent),sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}async function Qv(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Ge(t?.cwd,t?.sessionId);if(!n)return{...e,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await vt(n.file,Xv),o=Zv(r);return{...e,session:o.session,week:o.week,subscription:!!(o.session||o.week),capturedAt:n.mtimeMs||Date.now()}}catch(n){return{...e,session:null,week:null,subscription:!1,error:n?.message||String(n),capturedAt:Date.now()}}}async function zm(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?Qv(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Vm({fresh:e})}}function Xm(t){let e=t.split(".");if(e.length!==4)return null;let n=0;for(let r of e){let o=Number(r);if(!Number.isInteger(o)||o<0||o>255)return null;n=n*256+o}return n>>>0}function tT(t){let[e,n]=t.split("/"),r=Xm(e);if(r==null)return null;let o=n==null?32:Number(n);if(!Number.isInteger(o)||o<0||o>32)return null;let s=o===0?0:4294967295<<32-o>>>0;return a=>(a&s)===(r&s)}function eT(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(tT).filter(Boolean)}function nT(t){let e=t.headers["fly-client-ip"];if(typeof e=="string"&&e.trim())return e.trim();let n=t.headers["x-forwarded-for"];return typeof n=="string"&&n.trim()?n.split(",")[0].trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function Ym(t){let e=eT(t),n=e.length===0;return{open:n,clientIp:nT,allowed(r){if(n)return!0;let o=Xm((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}import rT from"node:http";import oT from"node:net";function Zm(t){if(!t)return null;let e=String(t).replace(/:\d+$/,"").toLowerCase();if(/^[\d.]+$/.test(e)||e.includes(":"))return null;let n=e.split(".");if(n.length<3)return null;let r=n[0];return/^[a-z0-9-]+$/.test(r)?r:null}function Qm(t){let e=Zm(t.headers.host);return e?{slug:e,view:hi(e)}:null}var sT=t=>`<!doctype html><meta charset="utf-8">
245
+ _${o} archived_`:"")}async function DS(t,e){let n=await un();if(!n)return et("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 et("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(Rm(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),et(`Starting *${F(u)}* on ${F(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:NS(u,i)}}case"archive":{if(!a.length)return et("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Sc(await r.listTracks(),a.join(" "));return i?(pn(CS(t,n,{track:i,responseUrl:e.response_url})),et(`Archiving *${F(i.name)}*\u2026`)):vc(c,a.join(" "))}case"handoff":{let i=r.knownAgents??OS,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return et(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=Sc(await r.listTracks(),l);return u?(pn(MS(t,n,{track:u,agent:c,responseUrl:e.response_url})),et(`Handing *${F(u.name)}* to ${F(c)}\u2026`)):vc(d,l)}case"stop":{if(!a.length)return et("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Sc(await r.listTracks(),a.join(" "));return i?(pn(PS(t,n,{track:i,responseUrl:e.response_url})),et(`Stopping *${F(i.name)}*\u2026`)):vc(c,a.join(" "))}case"list":return et(await BS(r));default:return et($S)}}async function jS(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Ec(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 le(),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,et(`Couldn't answer: ${c?.message||c}`))}}async function LS(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,Ao(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,et(`Couldn't act on the offer: ${c?.message||c}`))}}async function US(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,{...et(`Starting *${F(s)}*\u2026`),replace_original:!0}),pn(Rm(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(jS(t,r,e,o)):n.action_id==="bostrat-ship"&&pn(LS(t,r,e,o))}async function FS(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 IS(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 HS(t){return async e=>{if(e.type==="slash_commands")return DS(t,e.payload);if(e.type==="interactive"){await US(t,e.payload);return}if(e.type==="events_api"){await FS(t,e.payload?.event);return}}}var WS=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 qS(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:Wr(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 GS(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",qS(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await le()}async function KS(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 le(),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 VS(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(Wr(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await le()}async function JS(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await le();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 zS(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??XS)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await vm(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 XS(t,e){let n=await Ae(t);if(!n)return null;let r=rn(n,e);if(!r)return null;let o=await Hr.readFile(Dt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function YS(t,e){if(!WS.has(e.type))return;let n=await Ec(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 le(),await Ac(r,n);return;case"turn.agent":{if(!o.text)return;let s=RS(o.text);if(!s)return;await mn(r,n,s,[{type:"section",text:{type:"mrkdwn",text:F(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${fs(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return GS(r,n,e);case"prompt.answered":return KS(r,n,e);case"pr.offered":return VS(r,n,e);case"pr.offer.answered":return JS(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 le(),await Ac(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await mn(r,n,`Live preview ${o.name}: ${ms(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${F(o.name)}*: ${ms(o.slug)}
248
+ _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return zS(r,n,e,t.deps)}}function $m(t){let e={deps:t};Nt.on("event",s=>{YS(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await un()||(r&&clearInterval(r),r=null,n=Tm({getToken:async()=>(await un())?.appToken,onEnvelope:HS(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import ZS from"node:fs/promises";function QS(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 tv(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 ev(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?ee(e.cwd,e.sessionId):e?.cwd?Gs(e.cwd):null}async function Om(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 nv(r,o,t,i);break;default:break}return a}async function nv(t,e,n,r){let o=[],s=ev(n,e);if(s)try{o=QS(await ZS.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=tv(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 sv from"node:fs";import Ic from"node:fs/promises";import av from"node:http";import Qn from"node:path";import bs from"node:os";import{createRequire as iv}from"node:module";import Rc from"ws";import rv from"node:path";import ov from"node:os";var Z$=process.env.BOSTRAT_STATE_DIR||rv.join(process.env.HOME||ov.tmpdir(),".bostrat");function Cm(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 Mm(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 ys=(()=>{try{return iv(import.meta.url)("../package.json").version||null}catch{return null}})(),cv=process.env.BOSTRAT_STATE_DIR||Qn.join(process.env.HOME||bs.tmpdir(),".bostrat");function lv(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 uv({home:t=process.env.HOME||bs.homedir(),env:e=process.env}={}){let n=r=>{try{return sv.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,Pm=1e3,dv=3e4,Nm=100,fv=2e3,pv=25e3;function Bm({url:t,token:e,nodeId:n=bs.hostname(),name:r=bs.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=Mm(t);let i=s.replace(/^http/,"ws"),c=new Map,l=new Map,u=Qn.join(cv,"plane-link","cursor.json"),d={},f=new Map,w=null,m=!1,g=!1,x=Pm,A=null,S=null,T=null,E=!1;async function J(){try{d=JSON.parse(await Ic.readFile(u,"utf8"))}catch{d={}}}function M(){E=!0,!S&&(S=setTimeout(async()=>{if(S=null,!!E){E=!1;try{await Ic.mkdir(Qn.dirname(u),{recursive:!0}),await Ic.writeFile(u,JSON.stringify(d))}catch(N){a.error("[plane-link] cursor flush failed:",N?.message||N)}}},fv),S.unref?.())}function I(N){return!w||w.readyState!==Rc.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+=Nm){let nt=X.slice(lt,lt+Nm).map(wt=>({trackId:W.id,event:wt}));if(!h(nt))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,nt=Object.fromEntries(Object.entries(X).filter(([,Pt])=>Pt!=null)),wt=new AbortController;v.set(W,wt);try{if(N.host){await $(N,nt,wt);return}let Pt=await fetch(s+q,{method:_,headers:nt,body:lt?Buffer.from(lt,"base64"):void 0,signal:wt.signal});if(I({v:St,ch:"control",type:"http_head",reqId:W,status:Pt.status,headers:{"content-type":Pt.headers.get("content-type")||"application/octet-stream"}}),Pt.body){for await(let De of Pt.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:nt}=N,wt=new URL(s);return new Promise(Pt=>{let De=av.request({host:wt.hostname,port:wt.port,method:X,path:lt,headers:{...W,host:N.host},signal:_.signal},jt=>{let{connection:y,"keep-alive":j,"transfer-encoding":wn,upgrade:Zr,...je}=jt.headers;I({v:St,ch:"control",type:"http_head",reqId:q,status:jt.statusCode||502,headers:je}),jt.on("data",Os=>{I({v:St,ch:"control",type:"http_data",reqId:q,data:Buffer.from(Os).toString("base64")})||jt.destroy()}),jt.on("end",()=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:null}),Pt()}),jt.on("error",()=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:"view upstream error"}),Pt()})});De.on("error",jt=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:jt?.message||"view request failed"}),Pt()}),nt&&De.write(Buffer.from(nt,"base64")),De.end()})}function z(){if(g)return;w=new Rc(t,{headers:{authorization:`Bearer ${e}`}}),w.on("open",()=>{I({v:St,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:ys,providers:uv()})}),w.on("message",W=>{let _;try{_=JSON.parse(W)}catch{return}if(_.ch==="ctl"&&_.type==="hello_ok"){m=!0,x=Pm,a.log(`[plane-link] connected to plane as ${n}`),_.minNodeVersion&&ys&&lv(ys,_.minNodeVersion)&&a.error(`[plane-link] this node is bostrat-node ${ys}; 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"}),pv),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 Rc(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:nt,bin:wt}of lt)q.send(nt,{binary:wt})}),q.on("message",(lt,nt)=>{let wt=nt?lt:Buffer.from(String(lt));I({v:St,ch:"attach",type:"data",streamId:_.streamId,data:wt.toString("base64"),...nt?{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,nt=l.get(_.streamId);nt?nt.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,dv))};w.on("close",N),w.on("error",N)}return Nt.on("event",b),J().then(z),{stop:()=>{g=!0,Nt.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 mv(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 Dm=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),jm=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},$c=t=>Math.round(t*1e4)/1e4;async function Lm(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=Dm(),u=0,d=[];for(let f of t||[]){let w=await B(f),m=us(w,{boxLabels:mv(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);jm(l,m.totalTokens);for(let[g,x]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:Dm()},n[g].usd=$c(n[g].usd+x.usd),n[g].events+=x.events,jm(n[g].tokens,x.tokens);for(let[g,x]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=$c(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:$c(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 qr=t=>`$${t.toFixed(4)}`,tr=(t,e)=>String(t).padEnd(e);function Um(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 ${qr(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(qr(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)} ${qr(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(qr(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(qr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
250
+ `)}import vv from"node:fs/promises";import Tv from"node:path";var hv=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],wv={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 ks(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of hv){let o=0;for(let s of wv[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var gv=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),yv=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Fm(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))gv.has(r)&&(e+=o||0),yv.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Oc(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 xs=t=>t.payload?.ts??t.ts,bv=12e4,Ce=t=>Math.round(t*1e4)/1e4,kv=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function xv(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>xs(S)>=e,a=ks([Oc(n?.name),Oc(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=ks(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)=>xs(S)-xs(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=xs(S);switch(T.lastTs!=null&&J>T.lastTs&&s(S)&&(E.activeMs+=Math.min(J-T.lastTs,bv)),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(ks(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&&!jo(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??Fm(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 Wm(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}=xv(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,kv());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=Sv(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:Hm(x.models),providers:Hm(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 Sv=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},Hm=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Ce(n)]));var Cc={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function Av(t){let e=String(t||"7d");return e in Cc?{key:e,ms:Cc[e]}:{key:"7d",ms:Cc["7d"]}}function _v(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=To(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 Ev(t){if(!t?.cwd)return null;try{let n=(await vv.readFile(Tv.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 qm(t,{window:e="7d",now:n=Date.now()}={}){let{key:r,ms:o}=Av(e),s=[];for(let a of t||[]){let i=await B(a);s.push({track:a,events:_v(a,i),brief:await Ev(a)})}return{window:r,...Wm(s,{windowMs:o,now:n})}}import er from"node:fs/promises";import Iv from"node:fs";import ft from"node:path";import Rv from"node:os";import{execFile as $v}from"node:child_process";import{promisify as Ov}from"node:util";var Cv=Ov($v),Gm=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||Rv.tmpdir(),".bostrat"),Mc=!1;async function Gr(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 Mv=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Kr(t,e){let n=0,r=await er.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let o of r){if(Mv.has(o.name))continue;let s=ft.join(t,o.name);o.isDirectory()?n+=await Kr(s,ft.join(e,o.name)):o.isFile()&&(n+=await Gr(s,ft.join(e,o.name))?1:0)}return n}async function Km({stateDir:t=Gm,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Gm}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await er.mkdir(e,{recursive:!0});let o=0;o+=await Gr(ft.join(t,"tracks.json"),ft.join(e,"tracks.json"))?1:0,o+=await Gr(ft.join(t,"mocks.json"),ft.join(e,"mocks.json"))?1:0,o+=await Gr(ft.join(t,"runbooks.json"),ft.join(e,"runbooks.json"))?1:0,o+=await Kr(ft.join(t,"runbooks"),ft.join(e,"runbooks")),o+=await Kr(ft.join(t,"mock-archive"),ft.join(e,"mock-archive")),o+=await Gr(ft.join(t,"events.jsonl"),ft.join(e,"events.jsonl"))?1:0,o+=await Kr(ft.join(t,"events"),ft.join(e,"events"));for(let l of r||[])!l?.cwd||!l?.id||(o+=await Kr(ft.join(l.cwd,".bostrat"),ft.join(e,"workspaces",l.id,".bostrat")));let s=(...l)=>Cv("git",["-C",e,...l]);Iv.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 da(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||Mc))try{await s("push","-q",n,"HEAD:refs/heads/main"),c=!0,Mc=!1}catch{Mc=!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 Pv}from"node:child_process";import{promisify as Nv}from"node:util";import{randomUUID as Bv}from"node:crypto";import Dv from"node:fs";import jv from"node:os";import Lv from"node:path";var Pe=Nv(Pv),Ss=t=>new Promise(e=>setTimeout(e,t)),Ne=Ds,Pc=process.env.BOSTRAT_USAGE_PROBE_CWD||Lv.join(jv.homedir(),".bostrat","usage-probe"),Uv=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,Fv=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,Hv=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Vm=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Me=null,Vr=null,Jr=null;async function Wv(){try{return await Pe(O,C("has-session","-t",Ne)),!0}catch{return!1}}function qv(){Me&&clearTimeout(Me),Me=setTimeout(()=>Gv().catch(()=>{}),Uv),Me.unref&&Me.unref()}async function Gv(){Me&&(clearTimeout(Me),Me=null);try{await Pe(O,C("kill-session","-t",Ne))}catch{}}async function Kv(){let t=Date.now()+Hv;for(;;){let e=await Nc().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await Ss(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Ss(500)}}async function Vv(){if(await Wv())return;if(!await pe())throw new Error("host tmux server not running \u2014 start globo-tmux.service");Dv.mkdirSync(Pc,{recursive:!0}),await fe(Pc);let t=Bv();await Pe(O,C("new-session","-d","-s",Ne,"-c",Pc,"-x","200","-y","60"));let e=`${Bs} --session-id ${t}`;await Pe(O,C("send-keys","-t",Ne,e,"Enter")),await Kv()}async function Nc(){let{stdout:t}=await Pe(O,C("capture-pane","-t",Ne,"-p"));return t}async function Jv(){await Vv(),await Pe(O,C("send-keys","-t",Ne,"-l","/usage")),await Pe(O,C("send-keys","-t",Ne,"Enter"));let t=Date.now()+Fv,e="";for(;await Ss(400),e=await Nc(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await Ss(900),e=await Nc(),await Pe(O,C("send-keys","-t",Ne,"Escape")).catch(()=>{}),qv(),e}function zv(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 Xv(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 Jm({fresh:t=!1}={}){return!t&&Vr&&Date.now()-Vr.at<Vm?Vr.data:Jr||(Jr=(async()=>{try{let e=await Jv(),n=zv(e),r={...n,contributors:Xv(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Vr={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 Vr={at:Date.now()-(Vm-3e3),data:n},n}finally{Jr=null}})(),Jr)}var Yv=262144;function Zv(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 zm(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:Zv(t.resets_in_seconds)}:null}function Qv(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:zm(i.primary),week:zm(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 tT(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,Yv),o=Qv(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 Xm(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?tT(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 Ym(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 eT(t){let[e,n]=t.split("/"),r=Ym(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 nT(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(eT).filter(Boolean)}function rT(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 Zm(t){let e=nT(t),n=e.length===0;return{open:n,clientIp:rT,allowed(r){if(n)return!0;let o=Ym((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}import oT from"node:http";import sT from"node:net";function Qm(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 th(t){let e=Qm(t.headers.host);return e?{slug:e,view:gi(e)}:null}var aT=t=>`<!doctype html><meta charset="utf-8">
254
254
  <meta http-equiv="refresh" content="3">
255
255
  <title>${t.name} \u2014 starting</title>
256
256
  <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
257
257
  <div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
258
- <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function th(t,e,n){let r=Qm(t);if(!r)return n();let{view:o,slug:s}=r;if(!o)return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: no view named "${s}"`);if(o.state==="starting")return e.statusCode=503,e.setHeader("content-type","text/html"),e.end(sT(o));if(o.state!=="live"&&o.state!=="unhealthy")return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...t.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":t.headers.host,"x-forwarded-proto":t.headers["x-forwarded-proto"]||"http"},i=rT.request({host:"127.0.0.1",port:o.port,method:t.method,path:t.url,headers:a},c=>{e.writeHead(c.statusCode||502,c.headers),c.pipe(e)});i.on("error",()=>{e.headersSent?e.destroy():(e.statusCode=502,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),t.pipe(i),t.on("aborted",()=>i.destroy())}function eh(t,e,n){let r=Qm(t);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return e.write(`HTTP/1.1 404 Not Found\r
258
+ <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function eh(t,e,n){let r=th(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(aT(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=oT.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 nh(t,e,n){let r=th(t);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return e.write(`HTTP/1.1 404 Not Found\r
259
259
  \r
260
260
  bostrat: no live view "${s}"\r
261
- `),e.destroy(),!0;let a=oT.connect(o.port,"127.0.0.1",()=>{let c=[`${t.method} ${t.url} HTTP/1.1`];for(let[l,u]of Object.entries(t.headers))l!=="host"&&c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`);c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
261
+ `),e.destroy(),!0;let a=sT.connect(o.port,"127.0.0.1",()=>{let c=[`${t.method} ${t.url} HTTP/1.1`];for(let[l,u]of Object.entries(t.headers))l!=="host"&&c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`);c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
262
262
  `)+`\r
263
263
  \r
264
- `),n?.length&&a.write(n),e.pipe(a),a.pipe(e)}),i=()=>{a.destroy(),e.destroy()};return a.on("error",i),e.on("error",i),!0}function nh(t,e){let n=typeof e=="string"?{host:e}:e||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${t.slug}.${r.replace(/:\d+$/,"")}/`:`http://${t.slug}.${aT(n.host)}/`}function aT(t){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let e=String(t||"").replace(/:\d+$/,"").toLowerCase();if(!e||!e.includes(".")||/^[\d.]+$/.test(e))return"bostrat.globopi";let n=e.split(".");return Zm(e)&&hi(n[0])?n.slice(1).join("."):e}import{execFile as iT}from"node:child_process";import{promisify as cT}from"node:util";var jc=cT(iT),sh=3e4,lT=30*6e4,rh=sh,uT=5*6e4,dT=10080*6e4,fT=60*6e4,pT=3,ah="number,url,title,headRefName,headRefOid,state",Dc=60*6e4,oh=new Map;function mT(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Dc)}function hT(t,e){return t.armedAt&&e-t.armedAt<lT||mT(t,e)?rh:e-(t.firstSeen||0)>dT?fT:uT}async function wT(t,e){let{stdout:n}=await jc("gh",["pr","view",String(e),"--repo",t,"--json",ah],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:t})}});return JSON.parse(n||"null")}async function gT(t){let{stdout:e}=await jc("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",ah],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Gt({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}async function yT(){let t=new Map;for(let e of await dt().catch(()=>[]))t.set(e.id,e);return t}async function bT(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function kT(t,e){let n=await Si(t);return n?_r(n,e):null}async function xT(t,e,n){let r=await Si(t);if(!r)throw new Error(`no local checkout for ${t}`);return Er(r,e,{method:n})}async function ST(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function Ss(t,e,n,r,o){if(!await r.disarm(t.repo,t.number).catch(()=>null))return;let a=n.get(t.trackId);a&&await r.emitDisarmed(a,{number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo,reason:o}).catch(()=>{})}var vT=10*6e4;async function TT(t,e,n){let r=await n.detail(t.repo,t.number).catch(()=>null);if(!r)return;if(r.checks.failed>0){let s=r.checks.failing.map(a=>a.name).join(", ");await Ss(t,r,e,n,`check${r.checks.failed===1?"":"s"} failed: ${s}`);return}if(r.mergeable==="CONFLICTING"){await Ss(t,r,e,n,`conflicts with ${r.baseRefName}`);return}if(!r.checks.total){let s=Math.max(t.autoMerge?.armedAt||0,Date.parse(r.updatedAt||"")||0);if((n.now??Date.now())-s<vT)return;await Ss(t,r,e,n,r.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(r.checks.pending>0||r.mergeable==="UNKNOWN"||r.mergeStateStatus==="UNKNOWN"||r.isDraft)return;let o=await n.lookup(t.repo,t.number).catch(()=>null);if(o?.autoMerge){try{await n.merge(t.repo,t.number,o.autoMerge.method||"merge")}catch(s){if(/already merged|not open|is closed/i.test(String(s?.message||s)))return;await Ss(t,r,e,n,String(s?.message||s).trim()||"merge failed");return}await ih(t,{...r,state:"MERGED"},e,n)}}async function Pc(t,e,n){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function AT(t,e){try{return await jc("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Nc(t,e,n,r=()=>!0){return(await B(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var Bc=(t,e)=>({number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo});async function _T(t,e,n,r){let o=r.now??Date.now(),s=e.headRefOid||null,a=n.get(t.trackId)||null;if(s&&(t.ciFix?.headSha&&s!==t.ciFix.headSha&&await Eo(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Ro(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait);await vd(t.repo,t.number,s).catch(()=>{}),!i&&!c&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Nc(a,"pushed",t.number,u=>u.payload?.sha===s)&&await Pc(a,"pushed",{...Bc(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Dc)await Io(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await Io(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=t.ciFixAwait.since||0;a&&!await Nc(a,"ci-fixed",t.number,l=>(l.payload?.ts||0)>=c)&&await Pc(a,"ci-fixed",{...Bc(t,e),passed:i.checks.passed}).catch(()=>{}),await Io(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Dc)await $o(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await $o(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){let c=t.reconcileAwait.since||0;a&&!await Nc(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Pc(a,"reconciled",{...Bc(t,e),base:i.baseRefName||null}).catch(()=>{}),await $o(t.repo,t.number).catch(()=>{})}}}}async function ih(t,e,n,r){let o=n.get(t.trackId);o&&((await B(o).catch(()=>[])).some(i=>i.type==="pr.state.changed"&&i.payload?.change==="merged"&&i.payload?.number===Number(t.number))||await r.emitMerged(o,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo}).catch(()=>{})),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function ET(t={}){let{now:e=Date.now(),auth:n=bi,view:r=wT,list:o=gT,links:s=Td,trackMap:a=yT,emitMerged:i=bT,mark:c=Qe,archiveIfArmed:l=tn,batchThreshold:u=pT,detail:d=kT,merge:f=xT,lookup:w=Tt,disarm:m=Co,emitDisarmed:g=ST,localHasCommit:x=AT}=t;if(!(await n().catch(()=>null))?.authenticated)return 0;let T=(await s().catch(()=>[])).filter(I=>e-(oh.get(bt(I.repo,I.number))||0)>=hT(I,e));if(!T.length)return 0;let E=new Map;for(let I of T)oh.set(bt(I.repo,I.number),e),E.has(I.repo)||E.set(I.repo,[]),E.get(I.repo).push(I);let J=await a().catch(()=>new Map),M={now:e,emitMerged:i,mark:c,archiveIfArmed:l,detail:d,merge:f,lookup:w,disarm:m,emitDisarmed:g,localHasCommit:x};for(let[I,h]of E){let p;if(h.length>u)p=await o(I).catch(()=>null);else{p=new Map;for(let b of h){let v=await r(I,b.number).catch(()=>null);v?.number!=null&&p.set(Number(v.number),v)}}if(p)for(let b of h){let v=p.get(Number(b.number));v&&(v.state==="MERGED"?await ih(b,v,J,M):v.state==="CLOSED"?await c(b.repo,b.number,"closed").catch(()=>{}):(await _T(b,v,J,M).catch(()=>{}),b.autoMerge&&await TT(b,J,M).catch(()=>{})))}}return T.length}function ch(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await ET()}catch{}finally{t=!1}}},sh)}import de from"node:fs/promises";import nr from"node:path";import hh from"node:os";import lh from"node:fs/promises";import vs from"node:path";import{execFile as IT}from"node:child_process";import{promisify as RT}from"node:util";import{fileURLToPath as $T}from"node:url";var OT=RT(IT),dh=vs.dirname($T(import.meta.url)),CT=vs.resolve(dh,"..","..","client","dist"),MT="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",PT=new Map([...MT].map((t,e)=>[t,e]));function NT(t){let e=[],n=0,r=0;for(let o of t){let s=PT.get(o);if(s===void 0)return e;n+=(s&31)<<r,s&32?r+=5:(e.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return e}function BT(t){let e=[],n=0,r=0,o=0;for(let s of t.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=NT(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}e.push(a)}return e}function DT(t,e){if(!t||t.length===0||t[0][0]>e)return null;let n=0,r=t.length-1;for(;n<r;){let o=n+r+1>>1;t[o][0]<=e?n=o:r=o-1}return t[n]}var uh=new Map;async function jT(t){let e;try{e=await lh.stat(t)}catch{return null}let n=uh.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await lh.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:BT(r.mappings||"")};return uh.set(t,o),o}catch{return null}}var Lc=null;function LT(){return Lc||(Lc=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):OT("git",["rev-parse","--short","HEAD"],{cwd:vs.resolve(dh,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Lc}var UT=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function FT(t,{distDir:e=CT}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(UT)){let[a,i,c,l]=s,u=await jT(vs.join(e,"assets",`${i}.map`)),d=u?DT(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function fh(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await LT();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await FT(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var HT=()=>process.env.BOSTRAT_STATE_DIR||nr.join(process.env.HOME||hh.tmpdir(),".bostrat"),Be=()=>nr.join(HT(),"telemetry"),wh=()=>nr.join(Be(),".relay-offsets.json"),WT=50,qT=14,GT=3e4,ph=500,Ct=(t,e)=>typeof t=="string"?t.slice(0,e):"";function KT(t,e){if(!t||typeof t!="object")return null;let n=Ct(t.name,64);if(!n)return null;let r=Number.isFinite(t.ts)?t.ts:e,o={name:n,ts:Math.abs(r-e)<168*3600*1e3?r:e,receivedAt:e,anonId:Ct(t.anonId,64),sessionId:Ct(t.sessionId,64),userId:Ct(t.userId,64),release:Ct(t.release,64),appVersion:Ct(t.appVersion,32),route:Ct(t.route,128),deviceClass:t.deviceClass==="phone"||t.deviceClass==="desktop"?t.deviceClass:""};return t.props&&typeof t.props=="object"&&!Array.isArray(t.props)&&(JSON.stringify(t.props).length<=8192?o.props=t.props:o.props={clamped:!0}),t.error&&typeof t.error=="object"&&(o.error={kind:Ct(t.error.kind,256),message:Ct(t.error.message,8192),stack:Ct(t.error.stack,32768),fingerprint:Ct(t.error.fingerprint,64)}),Array.isArray(t.breadcrumbs)&&(o.breadcrumbs=t.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Ct(s?.n,64),r:Ct(s?.r,128)}))),o}var gh=t=>new Date(t).toISOString().slice(0,10),mh=0;async function yh(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,WT):[]).map(s=>KT(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await fh(s);await de.mkdir(Be(),{recursive:!0});let o=nr.join(Be(),`${gh(e)}.ndjson`);return await de.appendFile(o,r.map(s=>JSON.stringify(s)+`
265
- `).join(""),"utf8"),e-mh>6*3600*1e3&&(mh=e,VT(e).catch(()=>{})),r.length}async function VT(t=Date.now()){let e=gh(t-qT*24*3600*1e3),n=[];try{n=await de.readdir(Be())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<e);if(!r.length)return;for(let s of r)await de.rm(nr.join(Be(),s),{force:!0});let o=await kh();for(let s of r)delete o[s];await xh(o)}var Ts=new Map;function bh(t,e=Date.now()){let n=t||"unknown",r=Ts.get(n);return r||(Ts.size>512&&Ts.clear(),r={tokens:40,at:e},Ts.set(n,r)),r.tokens=Math.min(40,r.tokens+(e-r.at)/6e4*20),r.at=e,r.tokens<1?!1:(r.tokens-=1,!0)}async function kh(){try{return JSON.parse(await de.readFile(wh(),"utf8"))||{}}catch{return{}}}async function xh(t){await de.mkdir(Be(),{recursive:!0}),await de.writeFile(wh(),JSON.stringify(t),"utf8")}function JT(t,{env:e,host:n}={}){let r=t.name==="app.error"&&t.error,o={};for(let[a,i]of[["event",t.name],["route",t.route],["session",t.sessionId],["anon",t.anonId],["user",t.userId],["release",t.release],["appVersion",t.appVersion],["device",t.deviceClass]])i&&(o[a]=i);let s={timestamp:new Date(t.ts).toISOString(),service:"bostrat-client",source:"treetop-js",env:e||process.env.BOSTRAT_ENV||"pi",host:n||hh.hostname(),level:r?"error":"info",message:r?`${t.error.kind||"Error"}: ${t.error.message||"(no message)"}`.slice(0,8192):t.name,tags:o,attributes:{...t.props?{props:t.props}:{},receivedAt:t.receivedAt}};return r&&(s.error={kind:t.error.kind,message:t.error.message,stack:t.error.stack},t.error.fingerprint&&(s.fingerprint=t.error.fingerprint),t.error.symbolicated&&(s.attributes.symbolicated=!0),t.breadcrumbs&&(s.attributes.breadcrumbs=t.breadcrumbs)),s}async function zT({fetchImpl:t=fetch}={}){let e=await Vp();if(!e)return{sent:0,disabled:!0};let n;try{n=(await de.readdir(Be())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await kh(),o=0;for(let s of n){let a=nr.join(Be(),s),i=r[s]||0,c;try{let f=await de.open(a,"r");try{let w=(await f.stat()).size;if(w<=i)continue;c=Buffer.alloc(w-i),await f.read(c,0,c.length,i)}finally{await f.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
266
- `).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=ph){let w=d.slice(f,f+ph).map(g=>JT(g)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:w})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=w.length}r[s]=i+l+1,await xh(r)}return{sent:o,disabled:!1}}var Uc=null,Fc=!1;function Sh(){Uc||(Uc=setInterval(async()=>{if(!Fc){Fc=!0;try{await zT()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Fc=!1}}},GT),Uc.unref?.())}import _s from"node:fs/promises";import Eh from"node:path";import XT from"node:os";var Ih=()=>process.env.BOSTRAT_STATE_DIR||Eh.join(process.env.HOME||XT.tmpdir(),".bostrat"),Rh=()=>Eh.join(Ih(),"dictation-glossary.json"),YT=100,ZT=500,QT=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Es=null,Hc=null,Wc=Promise.resolve();function vh(){let t={},e=Date.now();for(let n of QT)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function $h(){return Es||(Hc??=_s.readFile(Rh(),"utf8").then(t=>JSON.parse(t)?.terms??vh()).catch(()=>vh()).then(t=>Es=t).finally(()=>Hc=null),Hc)}async function Th(){let t=JSON.stringify({terms:Es},null,2),e=Rh(),n=`${e}.${process.pid}.tmp`;await _s.mkdir(Ih(),{recursive:!0}),await _s.writeFile(n,t),await _s.rename(n,e)}function tA(){return Wc=Wc.then(Th,Th),Wc.catch(()=>{})}async function qc(){return Object.values(await $h()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var eA=t=>Math.max(1,Math.ceil(t.length/4));async function nA(){let t=[],e=0;for(let{term:n}of await qc()){let r=eA(n);if(!(e+r>ZT)&&(t.push(n),e+=r,t.length>=YT))break}return t}async function rA(t,e){let n=await $h(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var Ah=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),As=t=>t.toLowerCase();function _h(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Oh(t,e){let n=Ah(t),r=Ah(e);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=As(n[u])===As(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)As(n[c])===As(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(_h(r[l])&&i.add(r[l]),l++);for(;l<s;l++)_h(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await rA(u,"learned");return await tA(),[...i]}var Jr=null;function oA(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return t===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:t===401||t===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${t}: ${e.slice(0,300)}`}}async function Ch(){if(!Jr)return null;let t=await Br();if(!t||t.apiKey.slice(-6)!==Jr.keyTail)return null;let{keyTail:e,...n}=Jr;return n}async function Mh(t,e){let n=await Br();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await nA())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":e||"application/octet-stream"},body:t});if(!o.ok){let i=await o.text().catch(()=>""),c=oA(o.status,i);c.persistent&&(Jr={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return Jr=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var jh=ct.dirname(aA(import.meta.url)),Ph=Number(process.env.PORT)||3362,Is=Om(process.env),Lh=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,rr=Ym(process.env.BOSTRAT_ALLOW_IPS),k=fe();k.disable("x-powered-by");k.use((t,e,n)=>{if(rr.allowed(rr.clientIp(t)))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});k.use(th);k.use("/api/mocks",fe.json({limit:"1mb"}));k.use("/api/mocks",fe.raw({type:"image/png",limit:"24mb"}));k.use("/api/runbooks",fe.json({limit:"1mb"}));k.use("/api/telemetry",fe.json({limit:"256kb"}));k.use("/api/dictation",fe.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));k.use("/api/tracks/:id/upload",fe.raw({type:()=>!0,limit:"25mb"}));k.use(fe.json({limit:"64kb"}));k.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Ht,hostTmuxAlive:await ee(),autoTriggerPct:Lh})});k.get("/api/sessions",async(t,e)=>{e.json({sessions:await vn()})});k.post("/api/sessions",async(t,e)=>{let{displayName:n,cwd:r}=t.body||{};if(typeof n!="string")return e.status(400).json({error:"displayName (string) required"});try{let o=await Ue(n,r||void 0);e.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;e.status(a).json({error:s})}});k.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});if(!await G(n))return e.status(404).json({error:"session not found"});try{await Wt(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});k.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});let r=await G(n);if(!r)return e.status(404).json({error:"session not found"});let o=await cr(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});k.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});try{let r=await Ml(n);e.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){e.status(409).json({error:r?.message||String(r)})}});k.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=uo(n);if(!r)return e.json({running:!1});e.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});k.get("/api/tracks",async(t,e)=>{e.json({tracks:await dt(),agents:Fi,tickets:await lm()})});k.get("/api/cost",async(t,e)=>{try{let n=await jm(await dt());if(n.cursorUsage=Kd(),t.query.format==="text")return e.type("text/plain").send(Lm(n));e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/roundup",async(t,e)=>{try{e.json(await Wm(await dt(),{window:t.query.window}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/hook",async(t,e)=>{try{let n=await $m(t.body||{},yp);e.json({ok:!0,events:n.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});k.post("/api/telemetry",async(t,e)=>{try{if(!bh(rr.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await yh(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});k.get("/api/repos",async(t,e)=>{try{let n=await oe(),r=new Set((await Promise.all(n.map(a=>Y(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await fr()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));e.json({repos:[...n,...s]})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/github/token",async(t,e)=>{try{e.json(await mo(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});k.get("/api/github/status",(t,e)=>{e.json({configured:po()})});k.post("/api/github/refresh",(t,e)=>{Vl(),e.json({ok:!0})});function Uh(t,e,n){let r=t.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>e&&o?.base===e||n&&o?.repo===n):!r?.base||!e||r.base===e||n&&r.repo===n}async function Jc({repo:t=null,number:e=null,branch:n=null}){let r=t?await Y(t):null;if(r&&e!=null){let s=await Tt(r,e),a=s&&await rt(s.trackId);if(a)return a}return n?(await dt()).find(s=>s.workspace?.branch===n&&Uh(s,t,r))??null:null}async function hn(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Jc({repo:s,number:e,branch:o}).catch(()=>null);c&&await R({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...i}}).catch(()=>{})}async function uA(t){let e=(await dt()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await Y(n.path);if(r)for(let o of n.prs){if(await Tt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&Uh(a,n.path,r));s&&await Nt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}k.get("/api/git/prs",async(t,e)=>{try{let n=await bf();await uA(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,archive:a,headRefName:i,title:c,url:l}=t.body||{};try{if(s){let w=o||"merge";if(!Vo.has(w))throw new Error(`invalid merge method: ${w}`);let m=Number(r);if(!Number.isInteger(m)||m<=0)throw new Error("invalid PR number");let g=await Y((await Te(n)).path);if(!g)throw new Error("repo has no GitHub remote");let x=await Jc({repo:n,number:m,branch:i}).catch(()=>null);if(!x)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(await Nt({repo:g,number:m,trackId:x.id,url:l,headRef:i,title:c,source:"matched"}).catch(()=>{}),!await Oo(g,m,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Va(g,m,a===!0),en(),await hn("auto-armed",{number:m,url:l,title:c,branch:i,repo:n,track:x}),e.json({ok:!0,number:m,auto:!0})}let u=await Er(n,r,{method:o||"squash"});await hn("merged",{number:u.number,url:l,title:c,branch:i,repo:n});let d=await Y(n).catch(()=>null),f=!1;if(d&&(a===!0&&await Va(d,u.number,!0).catch(()=>{}),f=await tn(d,u.number).catch(()=>!1),await Qe(d,u.number,"merged").catch(()=>{})),a===!0&&!f){let w=await Jc({repo:n,number:u.number,branch:i}).catch(()=>null);w&&!w.archivedAt&&await Vn(w.id).catch(()=>{})}e.json(u)}catch(u){let d=u?.message||String(u),f=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(d)?400:502;e.status(f).json({error:d})}});k.post("/api/git/prs/disarm-auto",async(t,e)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=t.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Y((await Te(n)).path).catch(()=>null),l=c?await Co(c,i).catch(()=>null):null;try{await kf(n,i)}catch(u){if(!l)throw u}en(),await hn("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),e.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;e.status(l).json({error:c})}});k.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await Y(n).catch(()=>null),s=await vf(n,{method:r||"merge",onMerged:a=>{hn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&tn(o,a.number).catch(()=>{}).then(()=>Qe(o,a.number,"merged")).catch(()=>{})}});e.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;e.status(a).json({error:s})}});k.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:Sf()})});k.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s}=t.body||{};try{let a=n?await rt(n):null;if(!a)return e.status(400).json({error:"trackId required"});if(a.workspace?.kind==="multi")return e.status(400).json({error:"this Track tags multiple repos \u2014 ask the lead in chat to push and open the PR(s); the Git-tab door becomes per-repo in the next phase"});let i=await Tf(a.cwd,{title:r,body:o,base:s}),c=await Y(a.cwd).catch(()=>null);c&&i.number&&await Nt({repo:c,number:i.number,trackId:a.id,url:i.url,headRef:a.workspace?.branch??null,title:r,source:"api"}),await hn("opened",{number:i.number,url:i.url,title:r,track:a,branch:a.workspace?.branch}),e.json(i)}catch(a){let i=a?.message||String(a),c=/required|invalid/i.test(i)?400:502;e.status(c).json({error:i})}});k.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await _r(n,r),a=(await dt()).find(w=>!w.archivedAt&&w.workspace?.branch===o.headRefName);if(!a)return e.status(404).json({error:`no active Track owns branch "${o.headRefName}"`});let i=await Af(n,o.number);if(!i.length)return e.status(409).json({error:"no failing checks on this PR"});let c=ct.join(a.cwd,".bostrat","ci"),l=ct.join(".bostrat","ci",`pr-${o.number}-fail.log`);await It.promises.mkdir(c,{recursive:!0}),await It.promises.writeFile(ct.join(a.cwd,l),i.map(w=>`\u2550\u2550\u2550\u2550 ${w.name}${w.link?` \xB7 ${w.link}`:""} \u2550\u2550\u2550\u2550
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 rh(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}.${iT(n.host)}/`}function iT(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 Qm(e)&&gi(n[0])?n.slice(1).join("."):e}import{execFile as cT}from"node:child_process";import{promisify as lT}from"node:util";var Uc=lT(cT),ah=3e4,uT=30*6e4,oh=ah,dT=5*6e4,fT=10080*6e4,pT=60*6e4,mT=3,ih="number,url,title,headRefName,headRefOid,state",Lc=60*6e4,sh=new Map;function hT(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Lc)}function wT(t,e){return t.armedAt&&e-t.armedAt<uT||hT(t,e)?oh:e-(t.firstSeen||0)>fT?pT:dT}async function gT(t,e){let{stdout:n}=await Uc("gh",["pr","view",String(e),"--repo",t,"--json",ih],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:t})}});return JSON.parse(n||"null")}async function yT(t){let{stdout:e}=await Uc("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",ih],{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 bT(){let t=new Map;for(let e of await dt().catch(()=>[]))t.set(e.id,e);return t}async function kT(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 xT(t,e){let n=await Ti(t);return n?Er(n,e):null}async function ST(t,e,n){let r=await Ti(t);if(!r)throw new Error(`no local checkout for ${t}`);return Ir(r,e,{method:n})}async function vT(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 vs(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 TT=10*6e4;async function AT(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 vs(t,r,e,n,`check${r.checks.failed===1?"":"s"} failed: ${s}`);return}if(r.mergeable==="CONFLICTING"){await vs(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<TT)return;await vs(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 vs(t,r,e,n,String(s?.message||s).trim()||"merge failed");return}await ch(t,{...r,state:"MERGED"},e,n)}}async function Bc(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 _T(t,e){try{return await Uc("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Dc(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 jc=(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 ET(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 Io(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 Td(t.repo,t.number,s).catch(()=>{}),!i&&!c&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Dc(a,"pushed",t.number,u=>u.payload?.sha===s)&&await Bc(a,"pushed",{...jc(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Lc)await Ro(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 Ro(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=t.ciFixAwait.since||0;a&&!await Dc(a,"ci-fixed",t.number,l=>(l.payload?.ts||0)>=c)&&await Bc(a,"ci-fixed",{...jc(t,e),passed:i.checks.passed}).catch(()=>{}),await Ro(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Lc)await Oo(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Oo(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){let c=t.reconcileAwait.since||0;a&&!await Dc(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Bc(a,"reconciled",{...jc(t,e),base:i.baseRefName||null}).catch(()=>{}),await Oo(t.repo,t.number).catch(()=>{})}}}}async function ch(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 IT(t={}){let{now:e=Date.now(),auth:n=xi,view:r=gT,list:o=yT,links:s=Ad,trackMap:a=bT,emitMerged:i=kT,mark:c=Qe,archiveIfArmed:l=tn,batchThreshold:u=mT,detail:d=xT,merge:f=ST,lookup:w=Tt,disarm:m=Mo,emitDisarmed:g=vT,localHasCommit:x=_T}=t;if(!(await n().catch(()=>null))?.authenticated)return 0;let T=(await s().catch(()=>[])).filter(I=>e-(sh.get(bt(I.repo,I.number))||0)>=wT(I,e));if(!T.length)return 0;let E=new Map;for(let I of T)sh.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 ch(b,v,J,M):v.state==="CLOSED"?await c(b.repo,b.number,"closed").catch(()=>{}):(await ET(b,v,J,M).catch(()=>{}),b.autoMerge&&await AT(b,J,M).catch(()=>{})))}}return T.length}function lh(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await IT()}catch{}finally{t=!1}}},ah)}import ue from"node:fs/promises";import nr from"node:path";import wh from"node:os";import uh from"node:fs/promises";import Ts from"node:path";import{execFile as RT}from"node:child_process";import{promisify as $T}from"node:util";import{fileURLToPath as OT}from"node:url";var CT=$T(RT),fh=Ts.dirname(OT(import.meta.url)),MT=Ts.resolve(fh,"..","..","client","dist"),PT="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",NT=new Map([...PT].map((t,e)=>[t,e]));function BT(t){let e=[],n=0,r=0;for(let o of t){let s=NT.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 DT(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=BT(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 jT(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 dh=new Map;async function LT(t){let e;try{e=await uh.stat(t)}catch{return null}let n=dh.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await uh.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:DT(r.mappings||"")};return dh.set(t,o),o}catch{return null}}var Fc=null;function UT(){return Fc||(Fc=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):CT("git",["rev-parse","--short","HEAD"],{cwd:Ts.resolve(fh,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Fc}var FT=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function HT(t,{distDir:e=MT}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(FT)){let[a,i,c,l]=s,u=await LT(Ts.join(e,"assets",`${i}.map`)),d=u?jT(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 ph(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await UT();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await HT(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var WT=()=>process.env.BOSTRAT_STATE_DIR||nr.join(process.env.HOME||wh.tmpdir(),".bostrat"),Be=()=>nr.join(WT(),"telemetry"),gh=()=>nr.join(Be(),".relay-offsets.json"),qT=50,GT=14,KT=3e4,mh=500,Mt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function VT(t,e){if(!t||typeof t!="object")return null;let n=Mt(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:Mt(t.anonId,64),sessionId:Mt(t.sessionId,64),userId:Mt(t.userId,64),release:Mt(t.release,64),appVersion:Mt(t.appVersion,32),route:Mt(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:Mt(t.error.kind,256),message:Mt(t.error.message,8192),stack:Mt(t.error.stack,32768),fingerprint:Mt(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:Mt(s?.n,64),r:Mt(s?.r,128)}))),o}var yh=t=>new Date(t).toISOString().slice(0,10),hh=0;async function bh(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,qT):[]).map(s=>VT(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await ph(s);await ue.mkdir(Be(),{recursive:!0});let o=nr.join(Be(),`${yh(e)}.ndjson`);return await ue.appendFile(o,r.map(s=>JSON.stringify(s)+`
265
+ `).join(""),"utf8"),e-hh>6*3600*1e3&&(hh=e,JT(e).catch(()=>{})),r.length}async function JT(t=Date.now()){let e=yh(t-GT*24*3600*1e3),n=[];try{n=await ue.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 ue.rm(nr.join(Be(),s),{force:!0});let o=await xh();for(let s of r)delete o[s];await Sh(o)}var As=new Map;function kh(t,e=Date.now()){let n=t||"unknown",r=As.get(n);return r||(As.size>512&&As.clear(),r={tokens:40,at:e},As.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 xh(){try{return JSON.parse(await ue.readFile(gh(),"utf8"))||{}}catch{return{}}}async function Sh(t){await ue.mkdir(Be(),{recursive:!0}),await ue.writeFile(gh(),JSON.stringify(t),"utf8")}function zT(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||wh.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 XT({fetchImpl:t=fetch}={}){let e=await Jp();if(!e)return{sent:0,disabled:!0};let n;try{n=(await ue.readdir(Be())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await xh(),o=0;for(let s of n){let a=nr.join(Be(),s),i=r[s]||0,c;try{let f=await ue.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+=mh){let w=d.slice(f,f+mh).map(g=>zT(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 Sh(r)}return{sent:o,disabled:!1}}var Hc=null,Wc=!1;function vh(){Hc||(Hc=setInterval(async()=>{if(!Wc){Wc=!0;try{await XT()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Wc=!1}}},KT),Hc.unref?.())}import Es from"node:fs/promises";import Ih from"node:path";import YT from"node:os";var Rh=()=>process.env.BOSTRAT_STATE_DIR||Ih.join(process.env.HOME||YT.tmpdir(),".bostrat"),$h=()=>Ih.join(Rh(),"dictation-glossary.json"),ZT=100,QT=500,tA=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Is=null,qc=null,Gc=Promise.resolve();function Th(){let t={},e=Date.now();for(let n of tA)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function Oh(){return Is||(qc??=Es.readFile($h(),"utf8").then(t=>JSON.parse(t)?.terms??Th()).catch(()=>Th()).then(t=>Is=t).finally(()=>qc=null),qc)}async function Ah(){let t=JSON.stringify({terms:Is},null,2),e=$h(),n=`${e}.${process.pid}.tmp`;await Es.mkdir(Rh(),{recursive:!0}),await Es.writeFile(n,t),await Es.rename(n,e)}function eA(){return Gc=Gc.then(Ah,Ah),Gc.catch(()=>{})}async function Kc(){return Object.values(await Oh()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var nA=t=>Math.max(1,Math.ceil(t.length/4));async function rA(){let t=[],e=0;for(let{term:n}of await Kc()){let r=nA(n);if(!(e+r>QT)&&(t.push(n),e+=r,t.length>=ZT))break}return t}async function oA(t,e){let n=await Oh(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var _h=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),_s=t=>t.toLowerCase();function Eh(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Ch(t,e){let n=_h(t),r=_h(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]=_s(n[u])===_s(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;)_s(n[c])===_s(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(Eh(r[l])&&i.add(r[l]),l++);for(;l<s;l++)Eh(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await oA(u,"learned");return await eA(),[...i]}var zr=null;function sA(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 Mh(){if(!zr)return null;let t=await Dr();if(!t||t.apiKey.slice(-6)!==zr.keyTail)return null;let{keyTail:e,...n}=zr;return n}async function Ph(t,e){let n=await Dr();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 rA())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=sA(o.status,i);c.persistent&&(zr={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 zr=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var Lh=ct.dirname(iA(import.meta.url)),Nh=Number(process.env.PORT)||3362,Rs=Cm(process.env),Uh=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,rr=Zm(process.env.BOSTRAT_ALLOW_IPS),k=de();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(eh);k.use("/api/mocks",de.json({limit:"1mb"}));k.use("/api/mocks",de.raw({type:"image/png",limit:"24mb"}));k.use("/api/runbooks",de.json({limit:"1mb"}));k.use("/api/telemetry",de.json({limit:"256kb"}));k.use("/api/dictation",de.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));k.use("/api/tracks/:id/upload",de.raw({type:()=>!0,limit:"25mb"}));k.use(de.json({limit:"64kb"}));k.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Rt,hostTmuxAlive:await pe(),autoTriggerPct:Uh})});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 lr(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 Pl(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=fo(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:Wi,tickets:await um()})});k.get("/api/cost",async(t,e)=>{try{let n=await Lm(await dt());if(n.cursorUsage=Vd(),t.query.format==="text")return e.type("text/plain").send(Um(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 qm(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 Om(t.body||{},bp);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(!kh(rr.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await bh(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 re(),r=new Set((await Promise.all(n.map(a=>Y(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await pr()).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 ho(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:mo()})});k.post("/api/github/refresh",(t,e)=>{Jl(),e.json({ok:!0})});function Fh(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 Xc({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&&Fh(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 Xc({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 dA(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&&Fh(a,n.path,r));s&&await Bt({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 kf();await dA(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(!Jo.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 Xc({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 Bt({repo:g,number:m,trackId:x.id,url:l,headRef:i,title:c,source:"matched"}).catch(()=>{}),!await Co(g,m,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await za(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 Ir(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 za(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 Xc({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 Mo(c,i).catch(()=>null):null;try{await xf(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 Tf(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:vf()})});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 Af(a.cwd,{title:r,body:o,base:s}),c=await Y(a.cwd).catch(()=>null);c&&i.number&&await Bt({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 Er(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 _f(n,o.number);if(!i.length)return e.status(409).json({error:"no failing checks on this PR"});let c=ct.join(a.cwd,".bostrat","ci"),l=ct.join(".bostrat","ci",`pr-${o.number}-fail.log`);await It.promises.mkdir(c,{recursive:!0}),await It.promises.writeFile(ct.join(a.cwd,l),i.map(w=>`\u2550\u2550\u2550\u2550 ${w.name}${w.link?` \xB7 ${w.link}`:""} \u2550\u2550\u2550\u2550
267
267
  ${w.log??"(no fetchable log \u2014 see link)"}
268
268
  `).join(`
269
- `));let u=i.map(w=>w.name).join(", "),d=await $e(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}. Failing check${i.length===1?"":"s"}: ${u}. The failure log is at ${l} in this worktree \u2014 read it, fix the root cause, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),f=await Y(n).catch(()=>null);f&&(await Nt({repo:f,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await xd(f,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await hn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:i.map(w=>w.name)}}),e.json({ok:!0,leadBox:d.leadBox,queued:!!d.queued,checks:i.map(w=>w.name),log:l})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});k.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await _r(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let a=(await dt()).find(l=>!l.archivedAt&&l.workspace?.branch===o.headRefName);if(!a)return e.status(404).json({error:`no active Track owns branch "${o.headRefName}"`});let i=await $e(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}. Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),c=await Y(n).catch(()=>null);c&&(await Nt({repo:c,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Sd(c,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await hn("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),e.json({ok:!0,leadBox:i.leadBox,queued:!!i.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});k.post("/api/tracks/:id/pr-offer",async(t,e)=>{let{offerId:n,action:r}=t.body||{};try{let o=null;if(Array.isArray(t.body?.actions)){if(o=hd.filter(u=>t.body.actions.includes(u)),!o.length)return e.status(400).json({error:"actions must include commit, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return e.status(400).json({error:'offerId and actions ["commit"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=await rt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});let a=await B(s),i=a.find(u=>u.type==="pr.offered"&&u.payload?.offerId===n);if(!i)return e.status(404).json({error:"unknown offer"});if(a.some(u=>u.type==="pr.offer.answered"&&u.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let c=null;o&&(c=await $e(s.id,To(o,{branch:i.payload.branch,base:i.payload.base,toPr:i.payload.toPr||null}),{kind:o.includes("pr")?"pr-go":"push-go"}));let l=o?o.includes("pr")?"pr":o.includes("push")?"push":"commit":"dismiss";await R({id:s.id,cwd:s.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:i.boxId,payload:{ts:Date.now(),offerId:n,action:l,actions:o}}),e.json({ok:!0,offerId:n,action:l,actions:o,queued:!!c?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});var zr=t=>e=>({...e,url:nh(e,t.headers)});k.get("/api/views",async(t,e)=>{try{let n=await sf({trackId:t.query.track||void 0});e.json({views:n.map(zr(t)),maxLive:ve})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/views/:id",async(t,e)=>{let n=await af(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:zr(t)(n),session:Nn(n.slug)})});k.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await rt(n):null;if(!i&&s&&(i=(await dt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await wi({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});e.json({ok:!0,view:zr(t)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);e.status(l).json({error:c})}});k.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:zr(t)(await cf(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:zr(t)(await lf(t.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;e.status(o).json({error:r})}});k.delete("/api/views/:id",async(t,e)=>{try{e.json(await uf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var dA=[["linear",ln,oc],["jira",is,nc]],Gc=new Map;k.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await ec(),r={};for(let[o,s,a]of dA){let i=await s();if(!i){Gc.delete(o),r[o]={configured:!1};continue}let c=Gc.get(o);if(!c||Date.now()-c.at>300*1e3){let l;try{l={configured:!0,ok:!0,...await a(i)}}catch(u){l={configured:!0,ok:!1,error:u?.message||String(u)}}c={at:Date.now(),body:l},Gc.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await ec()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await Kp(o),i=n?await rt(n):null;!i&&r&&(i=(await dt()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ct.sep)))||null),i&&await R({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});k.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await rt(n):null;if(!s&&r&&(s=(await dt()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await Yn(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await rt(r):null;if(!o&&n&&(o=(await dt()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ct.sep)))||null),!o)return e.status(400).json({error:"track (or a cwd inside a Track workspace) required"});e.json({trackId:o.id,configured:await um(),ticket:await te(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});k.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await te(t.params.id)})});k.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Yn(n,(t.body||{}).ref,{source:"api"});e.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);e.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});k.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await hc(t.params.id)})});var Rs=t=>/no ticket linked|unknown offer|unknown state/i.test(t)?404:/already answered/i.test(t)?409:/required|no ticketing/i.test(t)?400:502;k.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await mm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Rs(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await wc(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Rs(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await hm(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Rs(r)).json({error:r})}});k.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await rt(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await wm(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Rs(s)).json({error:s})}});k.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await rt(n):null;if(!s&&r&&(s=(await dt()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,trackId:s.id,proposal:await ym(s,o)})}catch(s){let a=s?.message||String(s);e.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await gm()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});k.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await rt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await bm(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);e.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});k.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Bf({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/mocks/:id",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});k.get("/api/mocks/:id/png",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=rn(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(Bt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});k.get("/api/mocks/:id/html",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=rn(n,t.query.v??null);if(!r?.htmlRel)return e.status(404).json({error:"this version has no HTML source (image-sourced)"});e.set("content-security-policy","sandbox"),e.sendFile(Bt(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});k.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n?t.query:t.body||{},{trackId:o,name:s,cwd:a,width:i,group:c}=r;try{let l=o?await rt(o):null;if(!l&&a&&(l=(await dt()).find(w=>w.cwd&&(a===w.cwd||a.startsWith(w.cwd+ct.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(f=>f.name===l.leadBox),d=await Df({trackId:l.id,name:s,html:n?null:r.html,image:n,cwd:l.cwd,width:i,group:c,box:l.leadBox,agent:u?.agent||null});e.json({ok:!0,mock:d})}catch(l){let u=l?.message||String(l);e.status(/required|invalid|must be/i.test(u)?400:500).json({error:u})}});k.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await jf(t.params.id,{html:n?null:(t.body||{}).html,image:n,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:r})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|invalid|must be/i.test(o)?400:500;e.status(s).json({error:o})}});k.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Uf(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await $e(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),e.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;e.status(o).json({error:r})}});k.delete("/api/mocks/:id",async(t,e)=>{try{e.json(await Ff(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await dt(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ct.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await zf({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/runbooks/:id",async(t,e)=>{let n=await Ri(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});k.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Ri(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Kf(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),e.sendFile(Un(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});k.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await rt(n):null;if(!a&&s&&(a=(await dt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await Xf({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});k.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await Yf(t.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;e.status(o).json({error:r})}});k.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await Zf(t.params.id,(t.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;e.status(o).json({error:r})}});k.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await Qf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/dictation",async(t,e)=>{if(!Buffer.isBuffer(t.body)||t.body.length===0)return e.status(400).json({error:"expected a raw audio body"});try{e.json(await Mh(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});k.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await Br(),terms:await qc(),problem:await Ch()})});k.post("/api/dictation/learn",async(t,e)=>{let{transcript:n,finalText:r}=t.body||{};if(typeof n!="string"||typeof r!="string")return e.status(400).json({error:"transcript and finalText are required"});e.json({learned:await Oh(n,r)})});k.post("/api/tracks",async(t,e)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c}=t.body||{};try{let l=await Ki({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Yn(l,c,{source:"create"})}catch(d){u=d?.message||String(d)}e.json({ok:!0,track:l,...u?{ticketWarning:u}:{}})}catch(l){let u=l?.message||String(l);e.status(/required|unknown agent|invalid|must be/i.test(u)?400:500).json({error:u})}});k.get("/api/tracks/:id",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:Nr(t.params.id),subtasks:Jn(t.params.id).map(Xr)})});k.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Op(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await hc(t.params.id).catch(()=>{}),e.json(n)}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await Vn(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});k.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await Cp(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});k.get("/api/tracks/:id/messages",async(t,e)=>{let n=await Wi(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});k.get("/api/tracks/:id/events",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),e.flushHeaders();let r=i=>e.write(`id: ${i.seq}
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 Bt({repo:f,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Sd(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 Er(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 Bt({repo:c,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await vd(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=wd.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,Ao(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 Xr=t=>e=>({...e,url:rh(e,t.headers)});k.get("/api/views",async(t,e)=>{try{let n=await af({trackId:t.query.track||void 0});e.json({views:n.map(Xr(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 cf(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Xr(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 yi({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:Xr(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:Xr(t)(await lf(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:Xr(t)(await uf(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 df(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var fA=[["linear",ln,ac],["jira",cs,oc]],Vc=new Map;k.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await rc(),r={};for(let[o,s,a]of fA){let i=await s();if(!i){Vc.delete(o),r[o]={configured:!1};continue}let c=Vc.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},Vc.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 rc()})}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 Vp(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 dm(),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 gc(t.params.id)})});var $s=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 hm(t.params.id))}catch(n){let r=n?.message||String(n);e.status($s(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await yc(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status($s(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await wm(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status($s(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 gm(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status($s(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 bm(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 ym()})}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 km(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 Df({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(Dt(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(Dt(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 jf({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 Lf(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 Ff(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 Hf(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 Xf({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 Oi(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 Oi(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Vf(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 Yf({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 Zf(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 Qf(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 tp(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 Ph(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 Dr(),terms:await Kc(),problem:await Mh()})});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 Ch(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 Ji({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:Br(t.params.id),subtasks:Jn(t.params.id).map(Yr)})});k.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Cp(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await gc(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 Mp(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 Gi(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});k.get("/api/tracks/:id/events",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),e.flushHeaders();let r=i=>e.write(`id: ${i.seq}
270
270
  data: ${JSON.stringify(i)}
271
271
 
272
- `),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await B(n))i.seq>o&&r(i);let s=od(n.id,r),a=setInterval(()=>e.write(`: hb
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=sd(n.id,r),a=setInterval(()=>e.write(`: hb
273
273
 
274
- `),25e3);t.on("close",()=>{clearInterval(a),s()})});k.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await Pp(t.params.id)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/mid-turn|terminal|archived/i.test(r)?409:500;e.status(o).json({error:r})}});k.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await $e(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});k.post("/api/tracks/:id/upload",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(n.archivedAt)return e.status(409).json({error:"track is archived"});if(!n.cwd)return e.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(t.body)?t.body:null,o=await sp(n.cwd,{filename:t.query.filename,buffer:r});e.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);e.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});k.get("/api/tracks/:id/uploads/:file",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(!n.cwd)return e.status(404).json({error:"track has no workspace"});let r=ct.basename(String(t.params.file||""));if(!r||!Oi(ct.extname(r).slice(1)))return e.status(400).json({error:"not an upload path"});e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.sendFile(ct.join($i(n.cwd),r),o=>{o&&!e.headersSent&&e.status(404).json({error:"upload not found"})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});var fA=/^[0-9a-fA-F-]{8,64}$/;k.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});let{box:r,uuid:o}=t.params,s=Number(t.params.idx);if(!fA.test(o)||!Number.isInteger(s)||s<0)return e.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(f=>f.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return e.status(404).json({error:"image source not available"});let i=ne(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=sA.createInterface({input:It.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let f of u){if(!f.includes(c))continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.uuid===o){l=w,u.close();break}}let d=l?qs(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return e.status(404).json({error:"image not found"});e.set("content-type",d.mediaType),e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.send(Buffer.from(d.data,"base64"))}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await Gi(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});k.delete("/api/tracks/:id/queue",(t,e)=>{e.json({ok:!0,removed:os(t.params.id)})});k.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await qi(t.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code/i.test(a)?409:500;e.status(i).json({error:a})}});k.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await Rp({provider:t.params.provider,fresh:t.body?.fresh===!0});e.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);e.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});k.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await kp(t.params.id,{promptId:n,questionIndex:r}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/no pending prompt|changed since|not running|not supported|out of range|review screen/i.test(s)?409:500;e.status(a).json({error:s})}});k.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n}=t.body||{};try{e.json(await Tp(t.params.id,typeof n=="string"?n:""))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required/i.test(o)?400:500;e.status(s).json({error:o})}});k.get("/api/tracks/:id/usage",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=t.query.fresh==="1"||t.query.fresh==="true";e.json(await zm(r,{fresh:o}))});k.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await Ap(t.params.id,n))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|unknown model|not running/i.test(o)?400:500;e.status(s).json({error:o})}});k.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await Lp(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Xr(a)})}catch(a){let i=a?.message||String(a),c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;e.status(c).json({error:i})}});k.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:Jn(t.params.id).map(Xr)})});k.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await bp(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});k.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:Xr(Yi(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});k.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:Xr(Fp(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Xr(t){return t?{id:t.id,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,step:t.step,running:!t.finishedAt,box:t.box,branch:t.branch,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}k.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await Ji(t.params.id,n,r!==!1,o);e.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});k.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await vp(t.params.id,t.body||{}))}catch(n){let r=n?.message||String(n);e.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});k.get("/api/tracks/:id/handoff-lead",(t,e)=>{let n=Nr(t.params.id);if(!n)return e.json({running:!1});e.json({running:!n.finishedAt,step:n.step,error:n.error,fromBox:n.fromBox,toBox:n.toBox,toAgent:n.toAgent,skipPermissions:n.skipPermissions,startedAt:n.startedAt,finishedAt:n.finishedAt})});k.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:to(),paths:nl,trace:await el({limit:n})})});var Kc=ct.resolve(jh,"..","..","client","dist");It.existsSync(Kc)&&(k.use(fe.static(Kc)),k.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ct.join(Kc,"index.html"))));var Fh=iA.createServer(k),pA=new lA({noServer:!0}),Hh=(t,e,n)=>{if(!rr.allowed(rr.clientIp(t))){e.write(`HTTP/1.1 403 Forbidden\r
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 Np(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 ap(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||!Mi(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(Ci(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 pA=/^[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(!pA.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=ee(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=aA.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?Ks(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 Vi(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:ss(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 Ki(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 $p({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 xp(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 Ap(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 Xm(r,{fresh:o}))});k.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await _p(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 Up(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Yr(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(Yr)})});k.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await kp(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:Yr(Qi(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:Yr(Hp(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Yr(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 Xi(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 Tp(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=Br(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:eo(),paths:ol,trace:await rl({limit:n})})});var Jc=ct.resolve(Lh,"..","..","client","dist");It.existsSync(Jc)&&(k.use(de.static(Jc)),k.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ct.join(Jc,"index.html"))));var Hh=cA.createServer(k),mA=new uA({noServer:!0}),Wh=(t,e,n)=>{if(!rr.allowed(rr.clientIp(t))){e.write(`HTTP/1.1 403 Forbidden\r
275
275
  \r
276
- `),e.destroy();return}if(eh(t,e,n))return;let r=dl(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
276
+ `),e.destroy();return}if(nh(t,e,n))return;let r=fl(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
277
277
  \r
278
278
  `),e.destroy();return}let o=r.sessionName;if(!ut(o)){e.write(`HTTP/1.1 400 Bad Request\r
279
279
  \r
280
- `),e.destroy();return}let{cols:s,rows:a}=r;pA.handleUpgrade(t,e,n,i=>cl(i,o,{cols:s,rows:a}))};Fh.on("upgrade",Hh);var Wh=process.env.BOSTRAT_CERT_DIR||ct.join(process.env.HOME||Vc.tmpdir(),".bostrat","certs"),Nh=Number(process.env.BOSTRAT_TLS_PORT)||3443,Bh=ct.join(Wh,"bostrat.globopi.key"),Dh=ct.join(Wh,"bostrat.globopi.crt");if(It.existsSync(Bh)&&It.existsSync(Dh)){let t=cA.createServer({key:It.readFileSync(Bh),cert:It.readFileSync(Dh)},k);t.on("upgrade",Hh),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(Nh,Is,()=>{console.log(`[bostrat-agent] https listening on ${Is||"0.0.0.0"}:${Nh}`)})}Fh.listen(Ph,Is,()=>{console.log(`[bostrat-agent] listening on ${Is||"0.0.0.0"}:${Ph}`),console.log(`[bostrat-agent] tmux socket: ${Ht}`),console.log(`[bostrat-agent] auto-trigger default: ${Lh}%`),console.log(`[bostrat-agent] ip allowlist: ${rr.open?"OPEN (BOSTRAT_ALLOW_IPS unset)":process.env.BOSTRAT_ALLOW_IPS}`),Dp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),jp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Mp().then(t=>t&&console.log(`[bostrat-agent] reaped ${t} orphaned box(es) on archived track(s)`)).catch(t=>console.error("[bostrat-agent] archived-orphan sweep failed:",t?.message||t)),Ip().then(t=>console.log(`[bostrat-agent] standing instructions refreshed (${t.refreshed} updated, ${t.removed} legacy CLAUDE.md removed${t.codexGlobal?`, codex global ${t.codexGlobal}`:""})`)).catch(t=>console.error("[bostrat-agent] standing-instructions refresh failed:",t?.message||t)),gA(),wA(),ch(),km({resolveTrack:rt}),Rm({listTracks:dt,createTrack:Ki,archiveTrack:Vn,listRepos:oe,resolveTrack:rt,sayToLead:$e,answerPrompt:qi,startLeadHandoff:Ji,interruptLead:Gi,listSubtaskJobs:Jn,cancelSubtask:Yi,readEvents:B,appendEvent:R,knownAgents:Fi}),mA(),Sh(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Nm({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Vc.hostname(),name:process.env.BOSTRAT_NODE_NAME||Vc.hostname(),listTracks:dt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`))});function mA(){hA(),Wo().catch(()=>{}),setInterval(()=>Wo().catch(()=>{}),1e4)}function hA(){for(let t of["bostrat-view","bostrat-mock","bostrat-ticket","bostrat-runbook","gh"])try{let e=ct.resolve(jh,"..","bin",t),n=ct.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!It.existsSync(e))continue;It.mkdirSync(n,{recursive:!0});let r=ct.join(n,t);(It.existsSync(r)?It.readlinkSync(r):null)!==e&&(It.rmSync(r,{force:!0}),It.symlinkSync(e,r)),console.log(`[bostrat-agent] ${t} CLI linked at ${r}`)}catch(e){console.error(`[bostrat-agent] ${t} CLI link failed:`,e?.message||e)}}function wA(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await dt(),r=await Gm({tracks:n});r.committed&&console.log(`[state-sync] committed (${r.staged} file(s) staged${r.pushed?", pushed":""})`)}catch{}finally{t=!1}}};setInterval(e,6e4),e()}function gA(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await dt();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await Wi(n.id).catch(()=>{}),await _f(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
280
+ `),e.destroy();return}let{cols:s,rows:a}=r;mA.handleUpgrade(t,e,n,i=>ll(i,o,{cols:s,rows:a}))};Hh.on("upgrade",Wh);var qh=process.env.BOSTRAT_CERT_DIR||ct.join(process.env.HOME||zc.tmpdir(),".bostrat","certs"),Bh=Number(process.env.BOSTRAT_TLS_PORT)||3443,Dh=ct.join(qh,"bostrat.globopi.key"),jh=ct.join(qh,"bostrat.globopi.crt");if(It.existsSync(Dh)&&It.existsSync(jh)){let t=lA.createServer({key:It.readFileSync(Dh),cert:It.readFileSync(jh)},k);t.on("upgrade",Wh),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(Bh,Rs,()=>{console.log(`[bostrat-agent] https listening on ${Rs||"0.0.0.0"}:${Bh}`)})}Hh.listen(Nh,Rs,()=>{console.log(`[bostrat-agent] listening on ${Rs||"0.0.0.0"}:${Nh}`),console.log(`[bostrat-agent] tmux socket: ${Rt}`),console.log(`[bostrat-agent] auto-trigger default: ${Uh}%`),console.log(`[bostrat-agent] ip allowlist: ${rr.open?"OPEN (BOSTRAT_ALLOW_IPS unset)":process.env.BOSTRAT_ALLOW_IPS}`),jp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Lp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Pp().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)),Rp().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)),yA(),gA(),lh(),xm({resolveTrack:rt}),$m({listTracks:dt,createTrack:Ji,archiveTrack:Vn,listRepos:re,resolveTrack:rt,sayToLead:$e,answerPrompt:Ki,startLeadHandoff:Xi,interruptLead:Vi,listSubtaskJobs:Jn,cancelSubtask:Qi,readEvents:B,appendEvent:R,knownAgents:Wi}),hA(),ir().then(t=>{t||console.warn(`[boot] no host tmux server on ${Rt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),vh(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Bm({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||zc.hostname(),name:process.env.BOSTRAT_NODE_NAME||zc.hostname(),listTracks:dt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`))});function hA(){wA(),qo().catch(()=>{}),setInterval(()=>qo().catch(()=>{}),1e4)}function wA(){for(let t of["bostrat-view","bostrat-mock","bostrat-ticket","bostrat-runbook","gh"])try{let e=ct.resolve(Lh,"..","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 gA(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await dt(),r=await Km({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 yA(){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 Gi(n.id).catch(()=>{}),await Ef(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}