bostrat-node 0.2.23 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,88 +1,88 @@
1
- var Nx=Object.defineProperty;var Ef=(t,e)=>{for(var n in e)Nx(t,n,{get:e[n],enumerable:!0})};import ft from"node:path";import Gt from"node:fs";import zP from"node:readline";import{fileURLToPath as JP}from"node:url";import fe from"express";import XP from"node:http";import YP from"node:https";import Tf from"node:os";import{WebSocketServer as ZP}from"ws";import{execFile as nS}from"node:child_process";import{promisify as rS}from"node:util";import ma from"node:fs";import{randomUUID as oS}from"node:crypto";import Vt from"node:fs/promises";import Of from"node:os";import Lt from"node:path";import{execFile as Lx}from"node:child_process";import{promisify as Fx}from"node:util";import{createHash as Px,randomBytes as Dx,timingSafeEqual as Bx}from"node:crypto";var Zs="BOSTRAT_HOOK_TOKEN",Qs="x-bostrat-hook";function If(){return Dx(32).toString("hex")}function Ao(t){let e=typeof t=="string"?t.trim():"";return e?Px("sha256").update(e).digest("hex"):null}function Rf(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:Bx(Buffer.from(t),Buffer.from(e))}var gc=new Map,yc=new Map,jx=6e4;function ta(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(gc.set(t,(gc.get(t)||0)+1),yc.has(t)&&n-yc.get(t)<jx)return!1;yc.set(t,n);let o=Object.entries(e).filter(([,s])=>s!=null&&s!=="").map(([s,a])=>`${s}=${a}`).join(" ");return r(`[hook-audit] ${t} n=${gc.get(t)}${o?` ${o}`:""}`),!0}async function $f(t,{byToken:e,legacy:n}){let r=typeof t=="string"?t.trim():"";if(!r)return{mode:"legacy",target:await n()};let o=await e(r);return o?.track?{mode:"token",target:o}:{mode:"refused",target:null}}function ea(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Ux(){return process.env.CLAUDE_CONFIG_DIR?Lt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Lt.join(Of.homedir(),".claude.json")}function Hx(){return process.env.CLAUDE_CONFIG_DIR?Lt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Lt.join(Of.homedir(),".claude","settings.json")}async function on(t){if(!t||typeof t!="string")return;let e=Lt.resolve(t),n=Ux(),r=Hx(),o={};try{o=JSON.parse(await Vt.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 Vt.mkdir(Lt.dirname(n),{recursive:!0}),await Vt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Vt.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' -H "${Qs}: $${Zs}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let f of["SessionStart","UserPromptSubmit","Stop"])i.hooks[f]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await Vt.mkdir(Lt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(i,null,2)),await Wx(e)}async function Wx(t){try{let e=Lt.resolve(t),n=Lt.join(e,".bostrat","memory","claude"),r=Lt.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await Vt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Lt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Vt.mkdir(n,{recursive:!0}),await Vt.mkdir(Lt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(o,null,2)),await qx(e,["/.claude/settings.local.json"])}catch{}}async function qx(t,e){try{let{stdout:n}=await Fx(Lx)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Lt.resolve(t,n.trim()),o=await Vt.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
2
- `).includes(a));if(!s.length)return;await Vt.mkdir(Lt.dirname(r),{recursive:!0}),await Vt.appendFile(r,(o&&!o.endsWith(`
1
+ var tS=Object.defineProperty;var Df=(t,e)=>{for(var n in e)tS(t,n,{get:e[n],enumerable:!0})};import ft from"node:path";import zt from"node:fs";import AD from"node:readline";import{fileURLToPath as _D}from"node:url";import me from"express";import ED from"node:http";import ID from"node:https";import Mf from"node:os";import{WebSocketServer as RD}from"ws";import{execFile as Sv}from"node:child_process";import{promisify as vv}from"node:util";import ba from"node:fs";import{randomUUID as Tv}from"node:crypto";import Jt from"node:fs/promises";import Ff from"node:os";import Ut from"node:path";import{execFile as sS}from"node:child_process";import{promisify as aS}from"node:util";import{createHash as eS,randomBytes as nS,timingSafeEqual as rS}from"node:crypto";var oa="BOSTRAT_HOOK_TOKEN",sa="x-bostrat-hook";function Bf(){return nS(32).toString("hex")}function Eo(t){let e=typeof t=="string"?t.trim():"";return e?eS("sha256").update(e).digest("hex"):null}function jf(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:rS(Buffer.from(t),Buffer.from(e))}var Sc=new Map,vc=new Map,oS=6e4;function aa(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(Sc.set(t,(Sc.get(t)||0)+1),vc.has(t)&&n-vc.get(t)<oS)return!1;vc.set(t,n);let o=Object.entries(e).filter(([,s])=>s!=null&&s!=="").map(([s,a])=>`${s}=${a}`).join(" ");return r(`[hook-audit] ${t} n=${Sc.get(t)}${o?` ${o}`:""}`),!0}async function Lf(t,{byToken:e,legacy:n}){let r=typeof t=="string"?t.trim():"";if(!r)return{mode:"legacy",target:await n()};let o=await e(r);return o?.track?{mode:"token",target:o}:{mode:"refused",target:null}}function ia(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function iS(){return process.env.CLAUDE_CONFIG_DIR?Ut.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Ut.join(Ff.homedir(),".claude.json")}function cS(){return process.env.CLAUDE_CONFIG_DIR?Ut.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Ut.join(Ff.homedir(),".claude","settings.json")}async function sn(t){if(!t||typeof t!="string")return;let e=Ut.resolve(t),n=iS(),r=cS(),o={};try{o=JSON.parse(await Jt.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 Jt.mkdir(Ut.dirname(n),{recursive:!0}),await Jt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Jt.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,i.tui===void 0&&(i.tui="default"),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' -H "${sa}: $${oa}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let f of["SessionStart","UserPromptSubmit","Stop"])i.hooks[f]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await Jt.mkdir(Ut.dirname(r),{recursive:!0}),await Jt.writeFile(r,JSON.stringify(i,null,2)),await lS(e)}async function lS(t){try{let e=Ut.resolve(t),n=Ut.join(e,".bostrat","memory","claude"),r=Ut.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await Jt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Ut.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Jt.mkdir(n,{recursive:!0}),await Jt.mkdir(Ut.dirname(r),{recursive:!0}),await Jt.writeFile(r,JSON.stringify(o,null,2)),await uS(e,["/.claude/settings.local.json"])}catch{}}async function uS(t,e){try{let{stdout:n}=await aS(sS)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Ut.resolve(t,n.trim()),o=await Jt.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
2
+ `).includes(a));if(!s.length)return;await Jt.mkdir(Ut.dirname(r),{recursive:!0}),await Jt.appendFile(r,(o&&!o.endsWith(`
3
3
  `)?`
4
4
  `:"")+s.join(`
5
5
  `)+`
6
- `)}catch{}}import bc from"node:fs/promises";import Kx from"node:os";import na from"node:path";function Gx(){let t=process.env.CODEX_HOME||na.join(Kx.homedir(),".codex");return na.join(t,"config.toml")}function Mf(t){return`[projects."${t}"]`}function Vx(t,e){let n=Mf(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 yr(t){if(!t||typeof t!="string")return;let e=na.resolve(t),n=Gx(),r="";try{r=await bc.readFile(n,"utf8")}catch{}if(Vx(r,e))return;let o=Mf(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),f=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
6
+ `)}catch{}}import Tc from"node:fs/promises";import dS from"node:os";import ca from"node:path";function fS(){let t=process.env.CODEX_HOME||ca.join(dS.homedir(),".codex");return ca.join(t,"config.toml")}function Uf(t){return`[projects."${t}"]`}function pS(t,e){let n=Uf(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 br(t){if(!t||typeof t!="string")return;let e=ca.resolve(t),n=fS(),r="";try{r=await Tc.readFile(n,"utf8")}catch{}if(pS(r,e))return;let o=Uf(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),f=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
7
7
  trust_level = "trusted"
8
8
  `;r=r.slice(0,a)+f+r.slice(l)}else{let s=`
9
9
  ${o}
10
10
  trust_level = "trusted"
11
11
  `;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
12
12
  `)?`
13
- `:"")}try{await bc.mkdir(na.dirname(n),{recursive:!0}),await bc.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Cf from"node:fs/promises";import zx from"node:os";import _o from"node:path";function Jx(t){let n=_o.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||_o.join(zx.homedir(),".cursor");return _o.join(r,"projects",n)}async function br(t){if(!t||typeof t!="string")return;let e=_o.resolve(t),n=Jx(e),r=_o.join(n,".workspace-trusted");await Cf.mkdir(n,{recursive:!0}),await Cf.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
- `,"utf8")}import L from"node:fs/promises";import vv from"node:os";import X from"node:path";import{execFile as Sv}from"node:child_process";import{promisify as Tv}from"node:util";import av from"node:fs";import Pf from"node:fs/promises";import Io from"node:path";import iv from"node:os";import{fileURLToPath as cv}from"node:url";import{execFile as lv}from"node:child_process";import{promisify as uv}from"node:util";import xc from"node:fs/promises";import sn from"node:path";import{execFile as Xx}from"node:child_process";import{promisify as Yx}from"node:util";var Zx=Yx(Xx),Nf=process.env.HOME||"/home/globo",Qx=sn.join(process.env.BOSTRAT_STATE_DIR||sn.join(Nf,".bostrat"),"repos"),tv=[...(process.env.BOSTRAT_REPOS_ROOT||sn.join(Nf,"Documents")).split(","),Qx].map(t=>t.trim()).filter(Boolean),ev=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,nv=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function rv(t){try{return(await xc.stat(sn.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function ov(t){let e=i=>xc.readFile(sn.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(o,s){if(t.has(o))return;let a=await rv(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:sn.basename(o),branch:await ov(o)});return}if(s>=ev)return;let i;try{i=await xc.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||nv.has(c.name)||await e(sn.join(o,c.name),s+1)}for(let o of tv)await e(sn.resolve(o),0);let n=new Set,r=[];for(let o of t.values()){let s=(await et(o.path))?.toLowerCase();if(s){if(n.has(s))continue;n.add(s)}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function sv(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var kc=new Map;async function et(t){if(!t)return null;if(kc.has(t))return kc.get(t);let e=null;try{let{stdout:n}=await Zx("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=sv(n)}catch{e=null}return kc.set(t,e),e}async function Eo(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await Oe())if((await et(n.path))?.toLowerCase()===e)return n.path;return null}var Df=uv(lv),dv=process.env.BOSTRAT_STATE_DIR||Io.join(process.env.HOME||iv.tmpdir(),".bostrat"),fv=Io.join(dv,"repos"),Bf=/^[^/\s]+\/[^/\s]+$/,pv=5*6e4,mv=6e4,hv=10*6e4,jf=fetch,vc=new Map,Fn=null;function Qt(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 ra(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Lf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),kr=(t,e)=>Object.assign(new Error(t),{status:e});async function oa(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Bf.test(n))throw kr("repo must be owner/name",400);let r=vc.get(n);if(r&&r.expiresAt-Date.now()>pv)return r;if(!ra(e))throw kr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await jf(`${Qt(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Lf(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw kr(`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 kr(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 vc.set(n,a),a}async function Ro({env:t=process.env,force:e=!1}={}){if(!ra(t))return{configured:!1,repos:[],username:null};if(!e&&Fn&&Date.now()-Fn.at<mv)return Fn.data;try{let n=await jf(`${Qt(t)}/v1/github/repos`,{headers:Lf(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Fn={at:Date.now(),data:r},r}catch(n){return Fn?Fn.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Ff(){vc.clear(),Fn=null}async function me({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await et(t):null);if(!r)return{};let{token:o}=await oa(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Sc({env:t=process.env}={}){return(await Ro({env:t})).username??null}var wv=()=>cv(new URL("../bin/bostrat-git-credential",import.meta.url)),gv=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function yv(t){let[e,n]=String(t).split("/");return Io.join(fv,e,n)}async function Tc(t,{env:e=process.env}={}){let n=(...r)=>Df("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${wv()}" ${gv(e)}`),await n("config","credential.useHttpPath","true")}async function Ac(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Bf.test(String(t||"")))throw kr(`bad repo slug "${t}"`,400);let r=yv(t);if(av.existsSync(Io.join(r,".git")))return r;let{token:o}=await oa(t,{env:e});await Pf.mkdir(Io.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Df("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:hv,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Pf.rm(r,{recursive:!0,force:!0}).catch(()=>{}),kr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Tc(r,{env:e}),r}var bv=6e4,kv=5e3,_c=new Map,xv=(...t)=>fetch(...t);function Uf(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Hf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Wf(t,e,n){let r=Qt(n),o=new AbortController,s=setTimeout(()=>o.abort(),kv);try{return await xv(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function qf(t,{env:e=process.env,force:n=!1}={}){if(!Uf(e))return{facts:[],source:"unconfigured"};let r=_c.get(t);if(!n&&r&&Date.now()-r.at<bv)return{facts:r.facts,source:"cache"};try{let o=await Wf(`/v1/memory/${encodeURIComponent(t)}`,{headers:Hf(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return _c.set(t,{at:Date.now(),facts:a}),{facts:a,source:"plane"}}catch(o){return r?{facts:r.facts,source:"stale",error:o?.message}:{facts:[],source:"unreachable",error:o?.message}}}async function sa(t,e,{env:n=process.env}={}){if(!Uf(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await Wf(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Hf(n),"content-type":"application/json"},body:JSON.stringify({facts:e})},n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.results)?o.results:[];return _c.delete(t),{pushed:s.filter(a=>a.outcome==="accepted").length,rejected:s.filter(a=>a.outcome==="rejected"),source:"plane"}}catch(r){return{pushed:0,source:"unreachable",error:r?.message}}}var Av=Tv(Sv),Ec=process.env.BOSTRAT_STATE_DIR||X.join(process.env.HOME||vv.tmpdir(),".bostrat"),Kf=200,_v=25*1024;function aa(){return X.join(Ec,"memory","repos")}var Me="operator";function Un(t){return t===Me?X.join(Ec,"memory",Me):X.join(aa(),t)}var Jf=".tombstones.json";async function $o(t){try{return JSON.parse(await L.readFile(X.join(t,Jf),"utf8"))}catch{return{}}}async function Ic(t,e){let n=await $o(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await L.mkdir(t,{recursive:!0}),await L.writeFile(X.join(t,Jf),JSON.stringify(n))}function Gf(t){let e=String(t||"").trim();if(!e)return null;let r=(e.replace(/\/+$/,"").split(/[/:]/).pop()||"").replace(/\.git$/i,"").toLowerCase();return/^[a-z0-9][a-z0-9._-]*$/.test(r)?r:null}async function Vf(t){try{let{stdout:e}=await Av("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function Rc(t){if(!t)return[];let e=Gf(await Vf(t));if(e)return[e];let n=[],r=[];try{r=await L.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Gf(await Vf(X.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function ia(t){let e=/^---\n([\s\S]*?)\n---\n?/.exec(t);return e?{fm:e[1],body:t.slice(e[0].length)}:{fm:null,body:t}}function an(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function he(t,e=null){let n=e??await L.readFile(t,"utf8").catch(()=>""),{fm:r}=ia(n),o=an(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await L.stat(t)).mtimeMs}catch{return 0}}function Xf(t,e){let n=an(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Me){let o=an(t,"trust");if(!o||/^operator/i.test(o))return Me}return e}var xr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function $c(t,e,n,r=new Map){let o=(t||"").split(`
13
+ `:"")}try{await Tc.mkdir(ca.dirname(n),{recursive:!0}),await Tc.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Hf from"node:fs/promises";import mS from"node:os";import Io from"node:path";function hS(t){let n=Io.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Io.join(mS.homedir(),".cursor");return Io.join(r,"projects",n)}async function kr(t){if(!t||typeof t!="string")return;let e=Io.resolve(t),n=hS(e),r=Io.join(n,".workspace-trusted");await Hf.mkdir(n,{recursive:!0}),await Hf.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
+ `,"utf8")}import j from"node:fs/promises";import HS from"node:os";import X from"node:path";import{execFile as WS}from"node:child_process";import{promisify as qS}from"node:util";import _S from"node:fs";import qf from"node:fs/promises";import $o from"node:path";import ES from"node:os";import{fileURLToPath as IS}from"node:url";import{execFile as RS}from"node:child_process";import{promisify as $S}from"node:util";import _c from"node:fs/promises";import an from"node:path";import{execFile as wS}from"node:child_process";import{promisify as gS}from"node:util";var yS=gS(wS),Wf=process.env.HOME||"/home/globo",bS=an.join(process.env.BOSTRAT_STATE_DIR||an.join(Wf,".bostrat"),"repos"),kS=[...(process.env.BOSTRAT_REPOS_ROOT||an.join(Wf,"Documents")).split(","),bS].map(t=>t.trim()).filter(Boolean),xS=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,SS=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function vS(t){try{return(await _c.stat(an.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function TS(t){let e=i=>_c.readFile(an.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 Ne(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await vS(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:an.basename(o),branch:await TS(o)});return}if(s>=xS)return;let i;try{i=await _c.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||SS.has(c.name)||await e(an.join(o,c.name),s+1)}for(let o of kS)await e(an.resolve(o),0);let n=new Set,r=[];for(let o of t.values()){let s=(await rt(o.path))?.toLowerCase();if(s){if(n.has(s))continue;n.add(s)}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function AS(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var Ac=new Map;async function rt(t){if(!t)return null;if(Ac.has(t))return Ac.get(t);let e=null;try{let{stdout:n}=await yS("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=AS(n)}catch{e=null}return Ac.set(t,e),e}async function Ro(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await Ne())if((await rt(n.path))?.toLowerCase()===e)return n.path;return null}var Kf=$S(RS),OS=process.env.BOSTRAT_STATE_DIR||$o.join(process.env.HOME||ES.tmpdir(),".bostrat"),CS=$o.join(OS,"repos"),Gf=/^[^/\s]+\/[^/\s]+$/,MS=5*6e4,NS=6e4,PS=10*6e4,Vf=fetch,Ec=new Map,Un=null;function Dt(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 la(t=process.env){return!!(Dt(t)&&t.BOSTRAT_PLANE_TOKEN)}var zf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),xr=(t,e)=>Object.assign(new Error(t),{status:e});async function ua(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Gf.test(n))throw xr("repo must be owner/name",400);let r=Ec.get(n);if(r&&r.expiresAt-Date.now()>MS)return r;if(!la(e))throw xr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Vf(`${Dt(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:zf(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw xr(`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 xr(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 Ec.set(n,a),a}async function Oo({env:t=process.env,force:e=!1}={}){if(!la(t))return{configured:!1,repos:[],username:null};if(!e&&Un&&Date.now()-Un.at<NS)return Un.data;try{let n=await Vf(`${Dt(t)}/v1/github/repos`,{headers:zf(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Un={at:Date.now(),data:r},r}catch(n){return Un?Un.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Jf(){Ec.clear(),Un=null}async function we({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await rt(t):null);if(!r)return{};let{token:o}=await ua(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Ic({env:t=process.env}={}){return(await Oo({env:t})).username??null}var DS=()=>IS(new URL("../bin/bostrat-git-credential",import.meta.url)),BS=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function jS(t){let[e,n]=String(t).split("/");return $o.join(CS,e,n)}async function Rc(t,{env:e=process.env}={}){let n=(...r)=>Kf("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${DS()}" ${BS(e)}`),await n("config","credential.useHttpPath","true")}async function $c(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Gf.test(String(t||"")))throw xr(`bad repo slug "${t}"`,400);let r=jS(t);if(_S.existsSync($o.join(r,".git")))return r;let{token:o}=await ua(t,{env:e});await qf.mkdir($o.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Kf("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:PS,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await qf.rm(r,{recursive:!0,force:!0}).catch(()=>{}),xr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Rc(r,{env:e}),r}var LS=6e4,FS=5e3,Oc=new Map,US=(...t)=>fetch(...t);function Yf(t=process.env){return!!(Dt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Xf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Zf(t,e,n){let r=Dt(n),o=new AbortController,s=setTimeout(()=>o.abort(),FS);try{return await US(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function Qf(t,{env:e=process.env,force:n=!1}={}){if(!Yf(e))return{facts:[],source:"unconfigured"};let r=Oc.get(t);if(!n&&r&&Date.now()-r.at<LS)return{facts:r.facts,source:"cache"};try{let o=await Zf(`/v1/memory/${encodeURIComponent(t)}`,{headers:Xf(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return Oc.set(t,{at:Date.now(),facts:a}),{facts:a,source:"plane"}}catch(o){return r?{facts:r.facts,source:"stale",error:o?.message}:{facts:[],source:"unreachable",error:o?.message}}}async function da(t,e,{env:n=process.env}={}){if(!Yf(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await Zf(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Xf(n),"content-type":"application/json"},body:JSON.stringify({facts:e})},n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.results)?o.results:[];return Oc.delete(t),{pushed:s.filter(a=>a.outcome==="accepted").length,rejected:s.filter(a=>a.outcome==="rejected"),source:"plane"}}catch(r){return{pushed:0,source:"unreachable",error:r?.message}}}var KS=qS(WS),Cc=process.env.BOSTRAT_STATE_DIR||X.join(process.env.HOME||HS.tmpdir(),".bostrat"),tp=200,GS=25*1024;function fa(){return X.join(Cc,"memory","repos")}var Pe="operator";function Hn(t){return t===Pe?X.join(Cc,"memory",Pe):X.join(fa(),t)}var op=".tombstones.json";async function Co(t){try{return JSON.parse(await j.readFile(X.join(t,op),"utf8"))}catch{return{}}}async function Mc(t,e){let n=await Co(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await j.mkdir(t,{recursive:!0}),await j.writeFile(X.join(t,op),JSON.stringify(n))}function ep(t){let e=String(t||"").trim();if(!e)return null;let r=(e.replace(/\/+$/,"").split(/[/:]/).pop()||"").replace(/\.git$/i,"").toLowerCase();return/^[a-z0-9][a-z0-9._-]*$/.test(r)?r:null}async function np(t){try{let{stdout:e}=await KS("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function Nc(t){if(!t)return[];let e=ep(await np(t));if(e)return[e];let n=[],r=[];try{r=await j.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=ep(await np(X.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function pa(t){let e=/^---\n([\s\S]*?)\n---\n?/.exec(t);return e?{fm:e[1],body:t.slice(e[0].length)}:{fm:null,body:t}}function cn(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function ge(t,e=null){let n=e??await j.readFile(t,"utf8").catch(()=>""),{fm:r}=pa(n),o=cn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await j.stat(t)).mtimeMs}catch{return 0}}function sp(t,e){let n=cn(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Pe){let o=cn(t,"trust");if(!o||/^operator/i.test(o))return Pe}return e}var Sr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function Pc(t,e,n,r=new Map){let o=(t||"").split(`
15
15
  `),s=new Map;for(let c of(e||"").split(`
16
- `)){let l=xr(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=xr(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
16
+ `)){let l=Sr(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=Sr(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
17
17
  `).replace(/\n{3,}/g,`
18
18
 
19
- `)}function Yf(t,e){let n=an(e,"name")||t.replace(/\.md$/,""),r=an(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function Oo(t){let e=[];try{e=await L.readdir(t,{withFileTypes:!0})}catch{return[]}return e.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function Zf(t,e){let n=X.join(t,".bostrat","memory"),r=await Oo(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=new Map,s=new Map,a=0,i=new Map;for(let l of r){let u=X.join(n,l),f;try{f=await L.readFile(u,"utf8")}catch{continue}let{fm:d}=ia(f),p=Xf(d,e);if(!p)continue;let m=Un(p),h=X.join(m,l),y=await he(u,f);if(i.has(m)||i.set(m,await $o(m)),(i.get(m)[l]||0)>=y)continue;let k=await he(h).catch(()=>0);if(await L.access(h).then(()=>!0).catch(()=>!1)&&k>=y)continue;await L.mkdir(m,{recursive:!0}),await L.writeFile(h,f),a++;let I=o.get(m);I||o.set(m,I={won:new Set,generated:new Map}),I.won.add(l),I.generated.set(l,Yf(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await L.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=X.join(l,"MEMORY.md"),d=await L.readFile(f,"utf8").catch(()=>"");await L.writeFile(f,$c(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Ev(){let t=X.join(Ec,"workspaces"),e=[];try{e=await L.readdir(t,{withFileTypes:!0})}catch{return{harvested:0}}let n=0,r=new Map;for(let o of e){if(!o.isDirectory())continue;let s=X.join(t,o.name);if(!await L.access(X.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await Rc(s),c=await Zf(s,i||null);n+=c.harvested;for(let[l,u]of c.changedByKey){r.has(l)||r.set(l,new Set);for(let f of u)r.get(l).add(f)}}return{harvested:n,changedByKey:r}}var Iv=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,Rv=t=>{let e=new Map;for(let n of(t||"").split(`
20
- `)){let r=xr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function $v(t,e){let n=Un(t),r=await $o(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!Iv.test(l)||l==="MEMORY.md")continue;let u=X.join(n,l),f=await L.access(u).then(()=>!0).catch(()=>!1),d=f?await he(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await L.rm(u,{force:!0}),await Ic(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await L.mkdir(n,{recursive:!0}),await L.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=X.join(n,"MEMORY.md"),l=await L.readFile(c,"utf8").catch(()=>""),u=$c(l,[...s.values()].join(`
19
+ `)}function ap(t,e){let n=cn(e,"name")||t.replace(/\.md$/,""),r=cn(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function Mo(t){let e=[];try{e=await j.readdir(t,{withFileTypes:!0})}catch{return[]}return e.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function ip(t,e){let n=X.join(t,".bostrat","memory"),r=await Mo(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=new Map,s=new Map,a=0,i=new Map;for(let l of r){let u=X.join(n,l),f;try{f=await j.readFile(u,"utf8")}catch{continue}let{fm:d}=pa(f),p=sp(d,e);if(!p)continue;let m=Hn(p),h=X.join(m,l),g=await ge(u,f);if(i.has(m)||i.set(m,await Co(m)),(i.get(m)[l]||0)>=g)continue;let k=await ge(h).catch(()=>0);if(await j.access(h).then(()=>!0).catch(()=>!1)&&k>=g)continue;await j.mkdir(m,{recursive:!0}),await j.writeFile(h,f),a++;let R=o.get(m);R||o.set(m,R={won:new Set,generated:new Map}),R.won.add(l),R.generated.set(l,ap(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await j.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=X.join(l,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,Pc(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function VS(){let t=X.join(Cc,"workspaces"),e=[];try{e=await j.readdir(t,{withFileTypes:!0})}catch{return{harvested:0}}let n=0,r=new Map;for(let o of e){if(!o.isDirectory())continue;let s=X.join(t,o.name);if(!await j.access(X.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await Nc(s),c=await ip(s,i||null);n+=c.harvested;for(let[l,u]of c.changedByKey){r.has(l)||r.set(l,new Set);for(let f of u)r.get(l).add(f)}}return{harvested:n,changedByKey:r}}var zS=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,JS=t=>{let e=new Map;for(let n of(t||"").split(`
20
+ `)){let r=Sr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function YS(t,e){let n=Hn(t),r=await Co(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!zS.test(l)||l==="MEMORY.md")continue;let u=X.join(n,l),f=await j.access(u).then(()=>!0).catch(()=>!1),d=f?await ge(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await j.rm(u,{force:!0}),await Mc(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await j.mkdir(n,{recursive:!0}),await j.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=X.join(n,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=Pc(l,[...s.values()].join(`
21
21
  `),o);if(a.length){let f=new Set(a);u=u.split(`
22
- `).filter(d=>!f.has(xr(d))).join(`
23
- `)}await L.mkdir(n,{recursive:!0}),await L.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Oc(t,e=null){let n=Un(t),r=e?.length?e:await Oo(n);if(!r.length)return{pushed:0,source:"empty"};let o=Rv(await L.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=X.join(n,a),c;try{c=await L.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await he(i,c),indexLine:o.get(a)??null})}return sa(t,s)}async function Ov(t){let{facts:e,source:n}=await qf(t);e.length?await $v(t,e):n==="plane"&&await Oc(t)}function Mv(t){let e=t.split(`
24
- `);e.length>Kf&&(e=e.slice(0,Kf));let n=e.join(`
25
- `);for(;Buffer.byteLength(n,"utf8")>_v&&e.length;)e.pop(),n=e.join(`
26
- `);return n}var Cv=`# Track memory
22
+ `).filter(d=>!f.has(Sr(d))).join(`
23
+ `)}await j.mkdir(n,{recursive:!0}),await j.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Dc(t,e=null){let n=Hn(t),r=e?.length?e:await Mo(n);if(!r.length)return{pushed:0,source:"empty"};let o=JS(await j.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=X.join(n,a),c;try{c=await j.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await ge(i,c),indexLine:o.get(a)??null})}return da(t,s)}async function XS(t){let{facts:e,source:n}=await Qf(t);e.length?await YS(t,e):n==="plane"&&await Dc(t)}function ZS(t){let e=t.split(`
24
+ `);e.length>tp&&(e=e.slice(0,tp));let n=e.join(`
25
+ `);for(;Buffer.byteLength(n,"utf8")>GS&&e.length;)e.pop(),n=e.join(`
26
+ `);return n}var QS=`# Track memory
27
27
 
28
28
  Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
29
29
  index and the fact files beside it. Facts you save here stay Claude-local but ride the
30
30
  same sync; prefer adding durable cross-provider facts to \`../\` following its format.
31
- `;async function Nv(t,e){if(!t||!e?.length)return{materialized:!1,reason:"no-repo-key"};let n=X.join(t,".bostrat","memory"),r=0,o=0,s=[];for(let u of e){let f=Un(u),d=await $o(f);for(let[y,k]of Object.entries(d)){let v=X.join(n,y);await L.access(v).then(()=>!0).catch(()=>!1)&&await he(v)<=k&&(await L.rm(v,{force:!0}),o++)}let p=await Oo(f);if(!p.length)continue;await L.mkdir(n,{recursive:!0});for(let y of p){let k=X.join(f,y),v=X.join(n,y),I=await he(k),R=await he(v).catch(()=>0);await L.access(v).then(()=>!0).catch(()=>!1)&&R>=I||(await L.copyFile(k,v),r++)}let m=await L.readFile(X.join(f,"MEMORY.md"),"utf8").catch(()=>""),h=u===Me?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${h}
31
+ `;async function tv(t,e){if(!t||!e?.length)return{materialized:!1,reason:"no-repo-key"};let n=X.join(t,".bostrat","memory"),r=0,o=0,s=[];for(let u of e){let f=Hn(u),d=await Co(f);for(let[g,k]of Object.entries(d)){let S=X.join(n,g);await j.access(S).then(()=>!0).catch(()=>!1)&&await ge(S)<=k&&(await j.rm(S,{force:!0}),o++)}let p=await Mo(f);if(!p.length)continue;await j.mkdir(n,{recursive:!0});for(let g of p){let k=X.join(f,g),S=X.join(n,g),R=await ge(k),$=await ge(S).catch(()=>0);await j.access(S).then(()=>!0).catch(()=>!1)&&$>=R||(await j.copyFile(k,S),r++)}let m=await j.readFile(X.join(f,"MEMORY.md"),"utf8").catch(()=>""),h=u===Pe?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${h}
32
32
 
33
33
  ${m.trim()}`:m.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
34
34
 
35
35
  `)+`
36
- `,c=Mv(i);await L.mkdir(n,{recursive:!0}),await L.writeFile(X.join(n,"MEMORY.md"),c);let l=X.join(n,"claude");return await L.mkdir(l,{recursive:!0}),await L.writeFile(X.join(l,"MEMORY.md"),Cv,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var zf=0,Pv=3e4;async function Qf(t){try{let e=await Rc(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Me];if(Date.now()-zf>Pv){zf=Date.now(),await Dv();let{changedByKey:r}=await Ev();for(let[o,s]of r)await Oc(o,[...s])}for(let r of n)await Ov(r);return await Nv(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function Dv(){let t=0,e=[];try{e=await L.readdir(aa(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=X.join(aa(),n.name),o=Un(Me),s=new Set,a=new Map;for(let p of await Oo(r)){let m=X.join(r,p),h;try{h=await L.readFile(m,"utf8")}catch{continue}let{fm:y}=ia(h);if(Xf(y,n.name)!==Me)continue;let k=X.join(o,p),v=await he(k).catch(()=>0),I=await he(m,h);await L.mkdir(o,{recursive:!0}),I>v&&await L.writeFile(k,h),await L.rm(m,{force:!0}),s.add(p),a.set(p,Yf(p,y)),t++}if(!s.size)continue;let i=Date.now();await Ic(r,[...s].map(p=>[p,i])),await sa(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=X.join(r,"MEMORY.md"),l=await L.readFile(c,"utf8").catch(()=>""),u=l.split(`
37
- `).filter(p=>!s.has(xr(p))).join(`
38
- `);await L.writeFile(c,u).catch(()=>{});let f=X.join(o,"MEMORY.md"),d=await L.readFile(f,"utf8").catch(()=>"");await L.writeFile(f,$c(d,l,s,a))}return{moved:t}}async function tp(){let t=[],e=[Me];try{for(let n of await L.readdir(aa(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=Un(n),o=await Oo(r),s=await $o(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await L.readFile(X.join(r,i),"utf8").catch(()=>""),{fm:l}=ia(c);a.push({file:i,name:an(l,"name")||i.replace(/\.md$/,""),description:an(l,"description")||"",scope:an(l,"scope")||null,modified:await he(X.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function ep(t,e){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(e)||e==="MEMORY.md")throw new Error("bad fact filename");let n=Un(t),r=X.join(n,e);if(!await L.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await L.rm(r,{force:!0}),await Ic(n,[[e,s]]);let a=X.join(n,"MEMORY.md"),i=await L.readFile(a,"utf8").catch(()=>"");await L.writeFile(a,i.split(`
39
- `).filter(l=>xr(l)!==e).join(`
40
- `)).catch(()=>{});let c=await sa(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function np(t){try{let e=await Rc(t),{harvested:n,changedByKey:r}=await Zf(t,e[0]||null);for(let[o,s]of r)await Oc(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import Dc from"node:fs/promises";import fp from"node:path";import ip from"node:fs/promises";import cp from"node:os";import lp from"node:path";import Mc from"node:fs/promises";import Bv from"node:path";var jv=0;async function Ft(t,e,{mode:n}={}){await Mc.mkdir(Bv.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++jv}.tmp`;await Mc.writeFile(r,JSON.stringify(e,null,2)+`
41
- `,n!=null?{mode:n}:void 0),await Mc.rename(r,t)}var Lv=1e4,Fv=3e4,rp=2e3,Uv=5,op=100,Hv=60*6e4,Wv=()=>process.env.BOSTRAT_STATE_DIR||lp.join(process.env.HOME||cp.tmpdir(),".bostrat"),up=()=>lp.join(Wv(),"content-outbox.json"),qv=()=>process.env.BOSTRAT_NODE_ID||cp.hostname(),Kv=(...t)=>fetch(...t),Gv=Fv,Vv=Hv;function ht(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Co=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function No(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),Lv);try{return await Kv(`${Qt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var la=null,Cc=null,ca=null,Mo=null;async function dp(){return la||(Cc||=ip.readFile(up(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>la={ops:Array.isArray(t?.ops)?t.ops:[],parked:Array.isArray(t?.parked)?t.parked:[]}).finally(()=>Cc=null),Cc)}async function ua(){await Ft(up(),la).catch(()=>{})}function sp(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function Pc(t){let e=await dp(),n=sp(t);if(t.op==="delete")e.ops=e.ops.filter(r=>!(r.family===t.family&&r.id===t.id)),e.ops.push(t);else{e.ops=e.ops.filter(o=>!(o.op==="delete"&&o.family===t.family&&o.id===t.id));let r=e.ops.findIndex(o=>sp(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>rp&&(e.ops=e.ops.slice(-rp)),await ua()}function ap(){ca||(ca=setTimeout(()=>{ca=null,vr().catch(()=>{})},Gv),ca.unref?.())}function Nc(t){let e=new Error(`plane ${t}`);return e.refused=!0,e}async function zv(t,e=process.env){if(t.op==="record"){let n=await No(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...Co(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:qv()}]})},e);if(!n.ok)throw Nc(n.status);let o=(await n.json().catch(()=>null))?.results?.[0];return o?.outcome==="rejected"?{delivered:!1,rejected:o.reason}:{delivered:!0}}if(t.op==="blob"){let n=await ip.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await No(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...Co(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw Nc(r.status);return r.status===400?{delivered:!1,rejected:(await r.json().catch(()=>null))?.error||"rejected"}:{delivered:!0}}if(t.op==="delete"){let n=await No(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:Co(e)},e);if(!n.ok)throw Nc(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function vr({env:t=process.env,log:e=console,now:n=Date.now()}={}){return ht(t)?Mo||(Mo=(async()=>{let r=await dp();Jv(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await zv(a,t)}catch(c){if(!c?.refused)return ap(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<Uv)return ap(),await ua(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};e.error?.(`[content-plane] ALARM parking ${a.op} ${a.family}/${a.id} after ${a.refusals} refusals (${c.message}) \u2014 draining the ${r.ops.length-1} ops behind it`),r.ops.shift(),r.parked.push({...a,parkedAt:n,error:c.message}),r.parked.length>op&&(r.parked=r.parked.slice(-op)),s++,await ua();continue}i.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await ua()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Mo=null}),Mo):{flushed:0,source:"unconfigured"}}function Jv(t,e){if(!t.parked.length||t.ops.length)return;let n=t.parked.filter(r=>e-(r.parkedAt||0)>=Vv);if(n.length){t.parked=t.parked.filter(r=>!n.includes(r));for(let r of n)t.ops.push({...r,refusals:0,parkedAt:void 0,error:void 0})}}async function Pt(t,e,{trackId:n=null,env:r=process.env}={}){return ht(r)?(await Pc({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),vr({env:r})):{queued:!1,source:"unconfigured"}}async function cn(t,e,n,r,o,{env:s=process.env}={}){return ht(s)?(await Pc({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),vr({env:s})):{queued:!1,source:"unconfigured"}}async function oe(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return ht(r)?(await Pc({op:"delete",family:t,id:e,deletedAt:n}),vr({env:r})):{queued:!1,source:"unconfigured"}}async function $t(t,{env:e=process.env}={}){if(!ht(e))return{records:[],source:"unconfigured"};try{let n=await No(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:Co(e)},e);if(!n.ok)throw new Error(`plane ${n.status}`);let r=await n.json();return{records:Array.isArray(r?.records)?r.records:[],source:"plane"}}catch(n){return{records:[],source:"unreachable",error:n?.message}}}async function da(t,e,n,r,{env:o=process.env}={}){if(!ht(o))return null;try{let s=await No(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Co(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="md"||r==="html"?a.toString("utf8"):a}catch{return null}}var Xv=".bostrat/handoff",Yv="latest.md",Zv=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null;async function pp(t,e,{box:n=null,agent:r=null}={}){if(!t||!ht())return{pushed:!1};try{let o=Date.now();return await Pt("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await cn("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function mp(t){try{let e=Zv(t);if(!e||!ht())return{restored:!1};let n=fp.join(t,Xv,Yv);if(await Dc.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await $t("checkpoints");if(o!=="plane")return{restored:!1};let s=r.find(i=>!i.deletedAt&&i.data?.id===e);if(!s?.data?.v)return{restored:!1};let a=await da("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Dc.mkdir(fp.dirname(n),{recursive:!0}),await Dc.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import Po from"node:fs/promises";import Qv from"node:os";import wp from"node:path";var tS=[/[A-Za-z0-9_-]{20,}#[A-Za-z0-9_-]{20,}/g,/\bsk-[A-Za-z0-9_-]{16,}/g,/\bghs_[A-Za-z0-9]{16,}|\bghp_[A-Za-z0-9]{16,}|\bgithub_pat_[A-Za-z0-9_]{20,}/g,/\bFlyV1 [A-Za-z0-9+/=_-]{16,}/g,/\bey[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g];function Hn(t){let e=String(t??"");for(let n of tS)e=e.replace(n,"\u2039redacted\u203A");return e}var Bc=process.env.BOSTRAT_DEBUG_DIR||wp.join(Qv.homedir(),".bostrat","debug"),Sr=wp.join(Bc,"handoff.jsonl"),hp=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function fa(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function eS(t){try{await Po.mkdir(Bc,{recursive:!0}),await Po.appendFile(Sr,`${JSON.stringify(t)}
42
- `,"utf8");let n=(await Po.readFile(Sr,"utf8")).split(`
43
- `).filter(Boolean);n.length>hp&&await Po.writeFile(Sr,`${n.slice(-hp).join(`
36
+ `,c=ZS(i);await j.mkdir(n,{recursive:!0}),await j.writeFile(X.join(n,"MEMORY.md"),c);let l=X.join(n,"claude");return await j.mkdir(l,{recursive:!0}),await j.writeFile(X.join(l,"MEMORY.md"),QS,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var rp=0,ev=3e4;async function cp(t){try{let e=await Nc(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Pe];if(Date.now()-rp>ev){rp=Date.now(),await nv();let{changedByKey:r}=await VS();for(let[o,s]of r)await Dc(o,[...s])}for(let r of n)await XS(r);return await tv(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function nv(){let t=0,e=[];try{e=await j.readdir(fa(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=X.join(fa(),n.name),o=Hn(Pe),s=new Set,a=new Map;for(let p of await Mo(r)){let m=X.join(r,p),h;try{h=await j.readFile(m,"utf8")}catch{continue}let{fm:g}=pa(h);if(sp(g,n.name)!==Pe)continue;let k=X.join(o,p),S=await ge(k).catch(()=>0),R=await ge(m,h);await j.mkdir(o,{recursive:!0}),R>S&&await j.writeFile(k,h),await j.rm(m,{force:!0}),s.add(p),a.set(p,ap(p,g)),t++}if(!s.size)continue;let i=Date.now();await Mc(r,[...s].map(p=>[p,i])),await da(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=X.join(r,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=l.split(`
37
+ `).filter(p=>!s.has(Sr(p))).join(`
38
+ `);await j.writeFile(c,u).catch(()=>{});let f=X.join(o,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,Pc(d,l,s,a))}return{moved:t}}async function lp(){let t=[],e=[Pe];try{for(let n of await j.readdir(fa(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=Hn(n),o=await Mo(r),s=await Co(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await j.readFile(X.join(r,i),"utf8").catch(()=>""),{fm:l}=pa(c);a.push({file:i,name:cn(l,"name")||i.replace(/\.md$/,""),description:cn(l,"description")||"",scope:cn(l,"scope")||null,modified:await ge(X.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function up(t,e){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(e)||e==="MEMORY.md")throw new Error("bad fact filename");let n=Hn(t),r=X.join(n,e);if(!await j.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await j.rm(r,{force:!0}),await Mc(n,[[e,s]]);let a=X.join(n,"MEMORY.md"),i=await j.readFile(a,"utf8").catch(()=>"");await j.writeFile(a,i.split(`
39
+ `).filter(l=>Sr(l)!==e).join(`
40
+ `)).catch(()=>{});let c=await da(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function dp(t){try{let e=await Nc(t),{harvested:n,changedByKey:r}=await ip(t,e[0]||null);for(let[o,s]of r)await Dc(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import Uc from"node:fs/promises";import xp from"node:path";import wp from"node:fs/promises";import gp from"node:os";import yp from"node:path";import Bc from"node:fs/promises";import rv from"node:path";var ov=0;async function Ht(t,e,{mode:n}={}){await Bc.mkdir(rv.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++ov}.tmp`;await Bc.writeFile(r,JSON.stringify(e,null,2)+`
41
+ `,n!=null?{mode:n}:void 0),await Bc.rename(r,t)}var sv=1e4,av=3e4,fp=2e3,iv=5,pp=100,cv=60*6e4,lv=()=>process.env.BOSTRAT_STATE_DIR||yp.join(process.env.HOME||gp.tmpdir(),".bostrat"),bp=()=>yp.join(lv(),"content-outbox.json"),uv=()=>process.env.BOSTRAT_NODE_ID||gp.hostname(),dv=(...t)=>fetch(...t),fv=av,pv=cv;function mt(t=process.env){return!!(Dt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Po=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Do(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),sv);try{return await dv(`${Dt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var ha=null,jc=null,ma=null,No=null;async function kp(){return ha||(jc||=wp.readFile(bp(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>ha={ops:Array.isArray(t?.ops)?t.ops:[],parked:Array.isArray(t?.parked)?t.parked:[]}).finally(()=>jc=null),jc)}async function wa(){await Ht(bp(),ha).catch(()=>{})}function mp(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function Fc(t){let e=await kp(),n=mp(t);if(t.op==="delete")e.ops=e.ops.filter(r=>!(r.family===t.family&&r.id===t.id)),e.ops.push(t);else{e.ops=e.ops.filter(o=>!(o.op==="delete"&&o.family===t.family&&o.id===t.id));let r=e.ops.findIndex(o=>mp(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>fp&&(e.ops=e.ops.slice(-fp)),await wa()}function hp(){ma||(ma=setTimeout(()=>{ma=null,vr().catch(()=>{})},fv),ma.unref?.())}function Lc(t){let e=new Error(`plane ${t}`);return e.refused=!0,e}async function mv(t,e=process.env){if(t.op==="record"){let n=await Do(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...Po(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:uv()}]})},e);if(!n.ok)throw Lc(n.status);let o=(await n.json().catch(()=>null))?.results?.[0];return o?.outcome==="rejected"?{delivered:!1,rejected:o.reason}:{delivered:!0}}if(t.op==="blob"){let n=await wp.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Do(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...Po(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw Lc(r.status);return r.status===400?{delivered:!1,rejected:(await r.json().catch(()=>null))?.error||"rejected"}:{delivered:!0}}if(t.op==="delete"){let n=await Do(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:Po(e)},e);if(!n.ok)throw Lc(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function vr({env:t=process.env,log:e=console,now:n=Date.now()}={}){return mt(t)?No||(No=(async()=>{let r=await kp();hv(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await mv(a,t)}catch(c){if(!c?.refused)return hp(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<iv)return hp(),await wa(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};e.error?.(`[content-plane] ALARM parking ${a.op} ${a.family}/${a.id} after ${a.refusals} refusals (${c.message}) \u2014 draining the ${r.ops.length-1} ops behind it`),r.ops.shift(),r.parked.push({...a,parkedAt:n,error:c.message}),r.parked.length>pp&&(r.parked=r.parked.slice(-pp)),s++,await wa();continue}i.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await wa()}return{flushed:o,parked:s,pending:0}})().finally(()=>{No=null}),No):{flushed:0,source:"unconfigured"}}function hv(t,e){if(!t.parked.length||t.ops.length)return;let n=t.parked.filter(r=>e-(r.parkedAt||0)>=pv);if(n.length){t.parked=t.parked.filter(r=>!n.includes(r));for(let r of n)t.ops.push({...r,refusals:0,parkedAt:void 0,error:void 0})}}async function Bt(t,e,{trackId:n=null,env:r=process.env}={}){return mt(r)?(await Fc({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),vr({env:r})):{queued:!1,source:"unconfigured"}}async function ln(t,e,n,r,o,{env:s=process.env}={}){return mt(s)?(await Fc({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),vr({env:s})):{queued:!1,source:"unconfigured"}}async function se(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return mt(r)?(await Fc({op:"delete",family:t,id:e,deletedAt:n}),vr({env:r})):{queued:!1,source:"unconfigured"}}async function It(t,{env:e=process.env}={}){if(!mt(e))return{records:[],source:"unconfigured"};try{let n=await Do(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:Po(e)},e);if(!n.ok)throw new Error(`plane ${n.status}`);let r=await n.json();return{records:Array.isArray(r?.records)?r.records:[],source:"plane"}}catch(n){return{records:[],source:"unreachable",error:n?.message}}}async function Bo(t,e,n,r,{env:o=process.env}={}){if(!mt(o))return null;try{let s=await Do(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Po(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="md"||r==="html"?a.toString("utf8"):a}catch{return null}}var wv=".bostrat/handoff",gv="latest.md",yv=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null;async function Sp(t,e,{box:n=null,agent:r=null}={}){if(!t||!mt())return{pushed:!1};try{let o=Date.now();return await Bt("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await ln("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function vp(t){try{let e=yv(t);if(!e||!mt())return{restored:!1};let n=xp.join(t,wv,gv);if(await Uc.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await It("checkpoints");if(o!=="plane")return{restored:!1};let s=r.find(i=>!i.deletedAt&&i.data?.id===e);if(!s?.data?.v)return{restored:!1};let a=await Bo("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Uc.mkdir(xp.dirname(n),{recursive:!0}),await Uc.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import jo from"node:fs/promises";import bv from"node:os";import Ap from"node:path";var kv=[/[A-Za-z0-9_-]{20,}#[A-Za-z0-9_-]{20,}/g,/\bsk-[A-Za-z0-9_-]{16,}/g,/\bghs_[A-Za-z0-9]{16,}|\bghp_[A-Za-z0-9]{16,}|\bgithub_pat_[A-Za-z0-9_]{20,}/g,/\bFlyV1 [A-Za-z0-9+/=_-]{16,}/g,/\bey[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g];function Wn(t){let e=String(t??"");for(let n of kv)e=e.replace(n,"\u2039redacted\u203A");return e}var Hc=process.env.BOSTRAT_DEBUG_DIR||Ap.join(bv.homedir(),".bostrat","debug"),Tr=Ap.join(Hc,"handoff.jsonl"),Tp=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function ga(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function xv(t){try{await jo.mkdir(Hc,{recursive:!0}),await jo.appendFile(Tr,`${JSON.stringify(t)}
42
+ `,"utf8");let n=(await jo.readFile(Tr,"utf8")).split(`
43
+ `).filter(Boolean);n.length>Tp&&await jo.writeFile(Tr,`${n.slice(-Tp).join(`
44
44
  `)}
45
- `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${Sr}: ${e?.message||e}`)}}async function q(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&&fa())try{e.paneTail=Hn((await mt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(fa()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await eS(e)}async function gp({limit:t=80}={}){try{return(await Po.readFile(Sr,"utf8")).split(`
46
- `).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Hn(o.paneTail)),o}catch{return{raw:Hn(r)}}})}catch{return[]}}var yp={DEBUG_DIR:Bc,LOG_FILE:Sr};var ot=rS(nS);function _r(t){return new Promise(e=>setTimeout(e,t))}function pa(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 C=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";function kp(t){return`/tmp/tmux-${t}/default`}function sS({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=kp(r);return e?o:n?`${n}/bostrat/tmux-default`:o}var bp=typeof process.getuid=="function"?process.getuid():0,zt=sS({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return ma.existsSync(kp(bp))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:bp}),aS=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Lc=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",iS="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";function Fc(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${iS}=1`]}var Uc="bostrat-usage-probe",cS=new Set([Uc]);function Hc(t,e,n){let r=Wn(t),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);if(!r||o)return t;let s=`${t} --session-id ${e}`,a=/(^|\s)(--name|-n)(\s|=)/.test(t);return n&&!a&&wt(n)&&(s+=` --name ${n}`),s}function Wn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function ln(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function lS(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:Wn(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function xp(t,{loginShell:e=!1,failOpen:n=!1}={}){let r=await vp(t,{loginShell:e});return r===ha?!0:r===Tr?!1:n}var ha="present",Tr="absent",Ar="unknown",jc=5e3,uS=[250,1e3,3e3],dS=t=>new Promise(e=>setTimeout(e,t));async function vp(t,{loginShell:e=!1,timeoutMs:n=jc}={}){if(!t)return Tr;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await ot(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:n}),ha}catch(s){return s?.killed||s?.signal?Ar:s?.code===1||s?.code===127?Tr:Ar}}async function wa(t,{retries:e=uS,sleep:n=dS,probe:r=vp}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:jc});for(let a=0;s===Ar&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:jc*2});return s}var Wc={claude:"npm install -g @anthropic-ai/claude-code",codex:"npm install -g @openai/codex","cursor-agent":"curl https://cursor.com/install -fsS | bash",gh:"apt install gh (Debian: enable cli.github.com/packages) / brew install gh \u2014 see https://github.com/cli/cli#installation"};function P(...t){return["-S",zt,...t]}async function un(){if(!ma.existsSync(zt))return!1;try{return await ot(C,P("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function Do(){if(await un())return!0;try{ma.mkdirSync(zt.replace(/\/[^/]+$/,""),{recursive:!0}),await ot(C,P("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await un()}var fS=/^[A-Za-z0-9_.-]{1,64}$/;function wt(t){return typeof t=="string"&&fS.test(t)}async function Ce(){if(!ma.existsSync(zt))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 ot(C,P("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
47
- `).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d]=r.split("|"),p=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(p)?n-p*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null)}}).filter(r=>!cS.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function tt(t){return(await Ce()).find(n=>n.name===t)||null}var pS=/^[A-Za-z0-9 ._↻-]+$/,mS=64;function hS(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=mS&&pS.test(e)}function qc(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Sp(t){try{let{stdout:e}=await ot(C,P("list-sessions","-F","#{session_name}"));return e.split(`
48
- `).map(n=>n.trim()).includes(t)}catch{return!1}}var wS=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,gS=1e3,yS=/not found|Unknown command/i;function bS(t){let e=String(t||"").trim().split(/\s+/)[0]||"";if(e.includes("/"))return null;let n=[];for(let o=1;o<=e.length-3;o++)n.push(e.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function kS(t,e){let n=Date.now()+wS(),r=bS(e),o=!1,s=0;for(;;){if(await _r(gS),s+=1,!await Sp(t))return;let a=await mt(t,{lines:15}),i=!o&&yS.test(a)&&!!r?.test(a);if(!i&&s>1&&!await Er(t))return;if(i){o=!0,await q({flow:"spawn",phase:"launch-retry",sessionName:t,agent:ln(e),promptHead:e.slice(0,120)}).catch(()=>{}),await ot(C,P("send-keys","-t",t,"C-u")),await ot(C,P("send-keys","-t",t,"-l",e)),await ot(C,P("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await q({flow:"spawn",phase:"launch-failed",sessionName:t,agent:ln(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function qn(t,e=aS,n={}){if(!hS(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=qc(r);if(!wt(o))throw new Error("resulting tmux session name is invalid");if(!await Do())throw new Error(`host tmux server not running on ${zt} and auto-start failed \u2014 start one: ${C} -S ${zt} new-session -d -s keeper "sleep 2147483647"`);if(await Sp(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Lc,a=n.agent||ln(s),i=!!n.resumeSessionId,c=n.resumeSessionId||oS(),l=i?s:Hc(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&Wn(s),d=n.firstRun===!0;d||(f&&await on(e),a==="codex"&&await yr(e),a==="cursor"&&await br(e),await Qf(e),await mp(e));let p=f&&!d?ea():null,m=If(),h=["-e",`${Zs}=${m}`];p&&h.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&h.push(...Fc());try{await ot(C,P("new-session","-d","-s",o,"-c",e,...h))}catch(y){throw pa(p?pa(y,p):y,m)}return await Kn(o),await ot(C,P("set-option","-t",o,"@bostrat-session","1")),await ot(C,P("set-option","-t",o,"@bostrat-title",r)),await ot(C,P("set-option","-t",o,"@bostrat-sid",c)),await ot(C,P("set-option","-t",o,"@bostrat-agent",a)),await ot(C,P("send-keys","-t",o,l,"Enter")),lS(l)&&kS(o,l).catch(()=>{}),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function we(t){if(!wt(t))throw new Error("invalid session name");await ot(C,P("kill-session","-t",t))}async function Tp(t,e){if(wt(t))try{let n=e?P("set-option","-t",t,"@bostrat-sid",e):P("set-option","-u","-t",t,"@bostrat-sid");await ot(C,n)}catch{}}async function mt(t,{lines:e=40}={}){if(!wt(t))return"";try{let n=P("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await ot(C,n);return String(r||"")}catch{return""}}var xS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,vS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,_t=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,SS=Number(process.env.BOSTRAT_BOX_COLS)||120,TS=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function Ap(t){if(!wt(t))return 0;try{let{stdout:e}=await ot(C,P("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
49
- `).filter(Boolean).length}catch{return 0}}async function Kn(t,{cols:e=SS,rows:n=TS}={}){if(!wt(t))return!0;if(await Ap(t))return!1;try{let r=await _p(t);if(r.cols===e&&r.rows===n)return!0;await ot(C,P("resize-window","-t",t,"-x",String(e),"-y",String(n))),await ot(C,P("set-window-option","-t",t,"window-size","latest")),await _r(80)}catch{}return!0}async function AS(t){if(!wt(t))return 0;try{let{stdout:e}=await ot(C,P("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function Er(t){let e=await AS(t);if(!e)return!1;try{return await ot("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function _p(t){if(!wt(t))return{cols:0,rows:0};try{let{stdout:e}=await ot(C,P("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 ga(t,{cols:e=xS,rows:n=vS}={}){if(wt(t)&&!await Ap(t))try{let r=await _p(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await ot(C,P("resize-window","-t",t,"-x",String(o),"-y",String(s))),await ot(C,P("set-window-option","-t",t,"window-size","latest")),await _r(80))}catch{}}async function dn(t){return await ga(t),mt(t,{lines:_t})}var _S=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function At(t,e,{settleMs:n=40,kind:r}={}){if(!wt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>_S.has(s));for(let s=0;s<o.length;s++)await ot(C,P("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await _r(n);return q({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function ya(t,e,{kind:n}={}){if(!wt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await ot(C,P("send-keys","-t",t,"-N",String(r),"BSpace")),q({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function ba(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!wt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await ot(C,P("send-keys","-t",t,"-l",s)),n>0&&await _r(n);q({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function Et(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!wt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await ot(C,P("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await _r(s),await ot(C,P("send-keys","-t",t,"Enter"))}q({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 ES from"node-pty";async function Ip(t,e,n={}){if(!wt(e)){ka(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await un()){ka(t,`host tmux server not running on ${zt}`),t.close(1011,"tmux server unavailable");return}if(!await tt(e)){ka(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=Ep(n.cols,120,Rp),o=Ep(n.rows,32,$p),s;try{s=ES.spawn(C,["-S",zt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ka(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Kc(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Kc(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"&&OS(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{}$S(e)})}var IS=6,RS=250;async function $S(t,e=IS,n=RS){for(let r=0;r<e;r++){if(await Kn(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Rp=20,$p=6;function OS(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=Rp&&Number(e)>=$p}function Ep(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Kc(t,e){try{t.send(JSON.stringify(e))}catch{}}function ka(t,e){Kc(t,{type:"error",message:e})}function Op(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 Bo from"node:fs";import MS from"node:fs/promises";import Ir from"node:path";import CS from"node:os";var Mp={"claude-opus-5":1e6,"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-5":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},NS=2e5,Gc=process.env.BOSTRAT_CLAUDE_PROJECTS||Ir.join(CS.homedir(),".claude","projects");function Vc(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function zc(t){let e=Ir.join(Gc,Vc(t)),n;try{n=Bo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Ir.join(e,o);try{let{mtimeMs:a}=Bo.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=Ir.join(Gc,Vc(t),`${e}.jsonl`);return Bo.existsSync(n)?n:null}function Cp(t){if(!t)return[];let e=Ir.join(Gc,Vc(t)),n;try{n=Bo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Bo.statSync(Ir.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Ut(t,e=131072){let n=await MS.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 PS(t){let e=t.split(`
50
- `);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 jo(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=zc(t),o=!1,!r)return null}else return null;let s;try{s=PS(await Ut(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Mp,s.model),i=Mp[s.model]||NS;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 DS(t,e){return!t||!e?null:Ne(t,e)}function BS(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(`
51
- `).trim():""}function jS(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 LS(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Jc(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 Np(t,e){if(!t.uuid)return null;let n=Jc(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 FS=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Xc(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Ut(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
52
- `)){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"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let h=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof h=="string"&&FS.test(h)&&s.set(m.id,h.split("/").pop())}if(LS(l.content)){let m=Np(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=BS(l.content),d=u==="assistant"?jS(l.content):null,p=u==="user"?Np(c,s):null;!f&&!d&&!p||o.push({role:u,text:f,tools:d||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var US=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function Pp(t,e,{turns:n=16}={}){let r=DS(t,e);if(!r)return"";let o=await Xc(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}:** ${US(s.text||c,700)} ${i}`.trim()}).join(`
53
-
54
- `):""}import zA from"node:fs";import hn from"node:fs/promises";import wn from"node:path";import HS from"node:fs/promises";import{execFile as WS}from"node:child_process";import{promisify as qS}from"node:util";var Yc=qS(WS),KS=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Dp=new Set,Bp=Promise.resolve(),Zc=null;function Lp(t){Zc=typeof t=="function"?t:null}function GS(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function Fp({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||Dp.has(r)||(Dp.add(r),Bp=Bp.then(()=>VS({sessionName:t,from:e,to:r})).catch(()=>{}))}async function VS({sessionName:t,from:e,to:n}){await q({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Yc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await HS.realpath(r),s=GS(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Yc("npm",["install","-g","@openai/codex"],{env:a,timeout:KS});let i=(await Yc(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await q({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await jp({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await q({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await jp({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function jp(t){if(Zc)try{await Zc(t)}catch{}}var zS=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Up=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function JS(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
55
- `);if(!Up.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(zS);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"&&Up.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 xa=/(^|\n)\s*›/,qp=/^\s*Welcome to Codex\b/;function Rr(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
56
- `);for(let r=n.length-1;r>=0;r--)if(qp.test(n[r]))return n.slice(r).join(`
57
- `);return e}function XS(t){let e=String(t).trim();if(!e)return!1;let n=(e.match(/[A-Za-z]/g)||[]).length;return n>=3&&n/e.length>=.5}function YS(t){return/device code/i.test(t)?"works from the device you're reading this on":/sign in with chatgpt/i.test(t)?"opens a browser link on the node itself \u2014 can't finish from another device":""}var Hp=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function ZS(t,e=""){let n=Rr(t);if(xa.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
58
- `),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Hp);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let p=r[d].trim();if(p){if(Hp.test(r[d])||/^Press enter/.test(p))break;f=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:f})}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&&!qp.test(r[l])&&XS(u)&&i.push(u)}let c=i.join(" ").slice(0,400)||"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=>{let u=YS(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Wp=/Enter this one-time code/,QS=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function tT(t,e=""){let n=Rr(t);if(xa.test(n)||!Wp.test(n))return null;let r=n.split(`
59
- `),o=r.findIndex(i=>Wp.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(QS);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 eT(t,e=""){let n=Rr(t);return xa.test(n)||!/open the following link to authenticate/.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 nT(t,e=""){let n=Rr(t);return xa.test(n)||!/Paste or type your API key/.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:""}]}]}}var Qc=/Run Everything|Add a follow-up|Plan, search, build anything/;function rT(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||Qc.test(n))return null;let r=n.replace(/\r/g,"").split(`
60
- `).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}var oT=/click this link to log in|Signing in with the browser/;function sT(t,e=""){let n=String(t);if(!oT.test(n)||Qc.test(n))return null;let r=n.replace(/\r/g,"").split(`
61
- `),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function aT(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||Qc.test(n)?null:{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,promptId:"cursor:signin-qr",questions:[{question:"Cursor is showing a QR sign-in code in the Terminal tab \u2014 scan it from another device to log in. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function Kp(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=tT(e,n)||eT(e,n)||nT(e,n)||ZS(e,n);if(r)return r}if(t==="cursor"){let r=sT(e,n)||aT(e,n)||rT(e,n);if(r)return r}return JS(e,{provider:t,box:n})}function Gp(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 iT=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,tl=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Lo=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/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,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/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},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/,auth:/Press any key to log in|click this link to log in|Scan this QR code to log in|Signing in with the browser/}},cT=/Trust this workspace|\[a\]\s*Trust/,lT=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function uT(t){let e=String(t||"");if(!Ot.codex.block.test(e))return null;let n=Rr(e);if(!Ot.codex.block.test(n))return null;let r=n.match(lT);return{from:r?.[1]||null,to:r?.[2]||null}}function fn(t){return new Promise(e=>setTimeout(e,t))}var dT=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,va=new Map;function Vp(t,e){let n=Date.now(),r=dT();for(let[s,a]of va)n-a>=r&&va.delete(s);let o=`${t}::${e}`;return va.has(o)?!1:(va.set(o,n),!0)}var fT=68,pT=/^\s*[─━]{10,}\s*$/;function mT(t){let e=0;for(let n of String(t).split(`
62
- `))pT.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function el(t,e){let n=Ot[t];if(!n?.busy)return{state:"idle",reason:null};if(rl(t,e))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(e))return{state:"idle",reason:null};if(n.busy.test(e))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(e))return{state:"busy",reason:null};let r=mT(e);return t==="claude"&&r>0&&r<fT?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function nl(t,e){return el(t,e).state}var hT=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,wT=15;function Jp(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
- `).slice(-wT).join(`
64
- `);return hT.test(n)}function Fo(t,e){return nl(t,e)==="busy"}function Sa(t,e){return nl(t,e)!=="idle"}function Ta(t,e){let n=Ot[t];return n?.up?n.up.test(e):!0}function gT(t,e){let n=Ot[t];return n?rl(t,e)?!1:n.up.test(e)&&nl(t,e)==="idle":!0}function rl(t,e){let n=Ot[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="codex"?!0:!n.up.test(e):!1:!1}async function zp(t,e,n){let r=Date.now()+n,o=0;for(await Kn(t).catch(()=>{});;){let s=await mt(t);if(gT(e,s)){await fn(Lo);return}if(e==="cursor"&&cT.test(s)&&!Ot.cursor.up.test(s)&&o<3){if(!Vp(t,"cursor-trust")){await fn(tl);continue}o+=1,await q({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await Et(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await fn(Lo);continue}if(e==="codex"&&o<3){let a=uT(s);if(a){if(!Vp(t,`codex-update:${a.from}->${a.to}`)){await fn(tl);continue}o+=1,await q({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await Et(t,"3",{enter:!1,kind:"codex-update-skip"}),Fp({sessionName:t,from:a.from,to:a.to}),await fn(Lo);continue}}if(Date.now()>r){if(rl(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==="cursor"&&Ot.cursor.auth.test(s)?a="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, 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":e==="claude"&&Ot.claude.block.test(s)&&(a='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),q({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await Er(t)){let a=`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw q({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",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.`),q({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 fn(Lo);return}await fn(tl)}}function Xp({agent:t,agentCmd:e}={}){let n=e?ln(e):ln(t||"");return{label:n,realClaude:e?Wn(e):n==="claude",realCodex:e?/codex/.test(e)&&!/fake-codex|fake-lead/.test(e):n==="codex",realCursor:e?/cursor/.test(e)&&!/fake-cursor|fake-box/.test(e):n==="cursor"}}function Aa({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Xp({agent:n,agentCmd:r});return!e||!o&&!s?null:pn({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function pn({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=iT()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Xp({agent:r,agentCmd:o});if(i&&t&&await on(t),c&&t&&await yr(t),l&&t&&await br(t),(i||c||l)&&n){await zp(n,a,s);return}let u=Ot[r]?r:Ot[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await zp(n,u,s);return}await fn(Lo)}var yT=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),bT=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function Pe(t){return t==="codex"?{enter:!0,submitDelayMs:yT}:t==="claude"?{enter:!0,submitDelayMs:bT}:{enter:!0}}import Ea from"node:fs/promises";import nm from"node:path";var kT=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],xT={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 Gn(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of kT){let o=0;for(let s of xT[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var vT=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),ST=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Yp(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))vT.has(r)&&(e+=o||0),ST.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Uo(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 Zp=1.5,Qp=.75,TT=new Set("a an and are as at be been but by for from had has have he her his i if in into is it its of on or our so than that the their them then there these they this those to was were will with would you your we".split(" "));function em(t){let e=Uo(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||TT.has(r)||n.push(r);return n}function AT(t){let e=[];for(let n of t||[]){let r=n.payload||{},o=String(r.text||"").trim();if(n.type==="turn.user"){if(n.source==="hook"||!o)continue;e.push(tm(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(tm(n,"assistant",o))}}return e}function tm(t,e,n){let r=Gn(n);return{id:t.id,seq:t.seq,ts:t.payload?.ts??t.ts,role:e,boxId:t.boxId??null,provider:t.provider??null,text:n,tags:r?[r.category]:[]}}function Ho(t){let e=AT(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=em(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=e.length;return{chunks:e,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function _a(t,e,n={}){let{k:r=8,role:o=null,tag:s=null,boxId:a=null}=n;if(!t||!t.N)return[];let i=[...new Set(em(e))];if(!i.length)return[];let c=new Map;for(let u of i){let f=t.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(t.N-d+.5)/(d+.5));for(let[m,h]of f){let y=t.lengths[m],k=h+Zp*(1-Qp+Qp*(y/(t.avgdl||1))),v=p*(h*(Zp+1)/k);c.set(m,(c.get(m)||0)+v)}}let l=[];for(let[u,f]of c){let d=t.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var rm="Relevant prior discussion (verbatim, retrieved";async function _T(t){let e=nm.join(t,".bostrat","events"),n;try{n=await Ea.readdir(e)}catch{return Ho([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Ea.readFile(nm.join(e,o),"utf8");for(let a of s.split(`
65
- `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Ho(r)}function ET(t){let e=String(t||""),n=a=>{let i=e.match(a);return i?i[1].trim():""},r=n(/#+\s*Objective\s*\n([\s\S]*?)(?:\n#+\s|$)/i),o=n(/#+\s*Next steps?[^\n]*\n([\s\S]*?)(?:\n#+\s|$)/i);return`${r}
66
- ${o}`.trim()||e.slice(0,2e3)}function IT(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=_a(t,e,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),f=u.length>800?u.slice(0,800)+"\u2026":u,d=`- **${l}:** ${f}`;if(i+d.length>r||(a.push(d),i+=d.length,a.length>=n))break}return a.length?["",`## ${rm}, not a summary)`,"_Auto-selected from the Track's event log by lexical relevance to the Objective / Next steps. Verbatim excerpts, not re-summarization (docs/memory-segmentation-indexing-scope.md \xA71.3)._","",...a,""].join(`
67
- `):""}async function om(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Ea.readFile(e,"utf8");if(a.includes(rm))return{appended:!1,reason:"already-enriched"};let i=await _T(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=IT(i,ET(a),{k:r,maxChars:o,exclude:s});return c?(await Ea.appendFile(e,`
45
+ `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${Tr}: ${e?.message||e}`)}}async function K(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&&ga())try{e.paneTail=Wn((await wt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(ga()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await xv(e)}async function _p({limit:t=80}={}){try{return(await jo.readFile(Tr,"utf8")).split(`
46
+ `).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Wn(o.paneTail)),o}catch{return{raw:Wn(r)}}})}catch{return[]}}var Ep={DEBUG_DIR:Hc,LOG_FILE:Tr};var it=vv(Sv);function Er(t){return new Promise(e=>setTimeout(e,t))}function ya(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 M=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";function Rp(t){return`/tmp/tmux-${t}/default`}function Av({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=Rp(r);return e?o:n?`${n}/bostrat/tmux-default`:o}var Ip=typeof process.getuid=="function"?process.getuid():0,Yt=Av({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return ba.existsSync(Rp(Ip))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Ip}),_v=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",qc=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ev="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";function Kc(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${Ev}=1`]}var Gc="bostrat-usage-probe",Iv=new Set([Gc]);function Vc(t,e,n){let r=qn(t),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);if(!r||o)return t;let s=`${t} --session-id ${e}`,a=/(^|\s)(--name|-n)(\s|=)/.test(t);return n&&!a&&gt(n)&&(s+=` --name ${n}`),s}function qn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function un(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function Rv(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:qn(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function $p(t,{loginShell:e=!1,failOpen:n=!1}={}){let r=await Op(t,{loginShell:e});return r===ka?!0:r===Ar?!1:n}var ka="present",Ar="absent",_r="unknown",Wc=5e3,$v=[250,1e3,3e3],Ov=t=>new Promise(e=>setTimeout(e,t));async function Op(t,{loginShell:e=!1,timeoutMs:n=Wc}={}){if(!t)return Ar;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await it(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:n}),ka}catch(s){return s?.killed||s?.signal?_r:s?.code===1||s?.code===127?Ar:_r}}async function xa(t,{retries:e=$v,sleep:n=Ov,probe:r=Op}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Wc});for(let a=0;s===_r&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:Wc*2});return s}var zc={claude:"npm install -g @anthropic-ai/claude-code",codex:"npm install -g @openai/codex","cursor-agent":"curl https://cursor.com/install -fsS | bash",gh:"apt install gh (Debian: enable cli.github.com/packages) / brew install gh \u2014 see https://github.com/cli/cli#installation"};function D(...t){return["-S",Yt,...t]}async function dn(){if(!ba.existsSync(Yt))return!1;try{return await it(M,D("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function Lo(){if(await dn())return!0;try{ba.mkdirSync(Yt.replace(/\/[^/]+$/,""),{recursive:!0}),await it(M,D("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await dn()}var Cv=/^[A-Za-z0-9_.-]{1,64}$/;function gt(t){return typeof t=="string"&&Cv.test(t)}async function ae(){if(!ba.existsSync(Yt))return[];try{let t="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}|#{alternate_on}",{stdout:e}=await it(M,D("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
47
+ `).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,p]=r.split("|"),m=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(m)?n-m*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:p==="1"}}).filter(r=>!Iv.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function et(t){return(await ae()).find(n=>n.name===t)||null}var Mv=/^[A-Za-z0-9 ._↻-]+$/,Nv=64;function Pv(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=Nv&&Mv.test(e)}function Jc(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Cp(t){try{let{stdout:e}=await it(M,D("list-sessions","-F","#{session_name}"));return e.split(`
48
+ `).map(n=>n.trim()).includes(t)}catch{return!1}}var Dv=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,Bv=1e3,jv=/not found|Unknown command/i;function Lv(t){let e=String(t||"").trim().split(/\s+/)[0]||"";if(e.includes("/"))return null;let n=[];for(let o=1;o<=e.length-3;o++)n.push(e.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function Fv(t,e){let n=Date.now()+Dv(),r=Lv(e),o=!1,s=0;for(;;){if(await Er(Bv),s+=1,!await Cp(t))return;let a=await wt(t,{lines:15}),i=!o&&jv.test(a)&&!!r?.test(a);if(!i&&s>1&&!await Ir(t))return;if(i){o=!0,await K({flow:"spawn",phase:"launch-retry",sessionName:t,agent:un(e),promptHead:e.slice(0,120)}).catch(()=>{}),await it(M,D("send-keys","-t",t,"C-u")),await it(M,D("send-keys","-t",t,"-l",e)),await it(M,D("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await K({flow:"spawn",phase:"launch-failed",sessionName:t,agent:un(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Kn(t,e=_v,n={}){if(!Pv(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Jc(r);if(!gt(o))throw new Error("resulting tmux session name is invalid");if(!await Lo())throw new Error(`host tmux server not running on ${Yt} and auto-start failed \u2014 start one: ${M} -S ${Yt} new-session -d -s keeper "sleep 2147483647"`);if(await Cp(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||qc,a=n.agent||un(s),i=!!n.resumeSessionId,c=n.resumeSessionId||Tv(),l=i?s:Vc(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&qn(s),d=n.firstRun===!0;d||(f&&await sn(e),a==="codex"&&await br(e),a==="cursor"&&await kr(e),await cp(e),await vp(e));let p=f&&!d?ia():null,m=Bf(),h=["-e",`${oa}=${m}`];p&&h.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&h.push(...Kc());try{await it(M,D("new-session","-d","-s",o,"-c",e,...h))}catch(g){throw ya(p?ya(g,p):g,m)}return await Gn(o),await it(M,D("set-option","-t",o,"@bostrat-session","1")),await it(M,D("set-option","-t",o,"@bostrat-title",r)),await it(M,D("set-option","-t",o,"@bostrat-sid",c)),await it(M,D("set-option","-t",o,"@bostrat-agent",a)),await it(M,D("send-keys","-t",o,l,"Enter")),Rv(l)&&Fv(o,l).catch(()=>{}),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function ye(t){if(!gt(t))throw new Error("invalid session name");await it(M,D("kill-session","-t",t))}async function Mp(t,e){if(gt(t))try{let n=e?D("set-option","-t",t,"@bostrat-sid",e):D("set-option","-u","-t",t,"@bostrat-sid");await it(M,n)}catch{}}async function wt(t,{lines:e=40}={}){if(!gt(t))return"";try{let n=D("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await it(M,n);return String(r||"")}catch{return""}}var Uv=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,Hv=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Rt=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,Wv=Number(process.env.BOSTRAT_BOX_COLS)||120,qv=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function Np(t){if(!gt(t))return 0;try{let{stdout:e}=await it(M,D("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
49
+ `).filter(Boolean).length}catch{return 0}}async function Gn(t,{cols:e=Wv,rows:n=qv}={}){if(!gt(t))return!0;if(await Np(t))return!1;try{let r=await Pp(t);if(r.cols===e&&r.rows===n)return!0;await it(M,D("resize-window","-t",t,"-x",String(e),"-y",String(n))),await it(M,D("set-window-option","-t",t,"window-size","latest")),await Er(80)}catch{}return!0}async function Kv(t){if(!gt(t))return 0;try{let{stdout:e}=await it(M,D("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function Ir(t){let e=await Kv(t);if(!e)return!1;try{return await it("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function Pp(t){if(!gt(t))return{cols:0,rows:0};try{let{stdout:e}=await it(M,D("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 Sa(t,{cols:e=Uv,rows:n=Hv}={}){if(gt(t)&&!await Np(t))try{let r=await Pp(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await it(M,D("resize-window","-t",t,"-x",String(o),"-y",String(s))),await it(M,D("set-window-option","-t",t,"window-size","latest")),await Er(80))}catch{}}async function fn(t){return await Sa(t),wt(t,{lines:Rt})}var Gv=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function kt(t,e,{settleMs:n=40,kind:r}={}){if(!gt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>Gv.has(s));for(let s=0;s<o.length;s++)await it(M,D("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await Er(n);return K({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}var va=1600;async function Rr(t,e,{kind:n}={}){if(!gt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await it(M,D("send-keys","-t",t,"-N",String(r),"BSpace")),K({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Ta(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!gt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await it(M,D("send-keys","-t",t,"-l",s)),n>0&&await Er(n);K({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function $t(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!gt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await it(M,D("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await Er(s),await it(M,D("send-keys","-t",t,"Enter"))}K({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 Vv from"node-pty";async function Bp(t,e,n={}){if(!gt(e)){Aa(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await dn()){Aa(t,`host tmux server not running on ${Yt}`),t.close(1011,"tmux server unavailable");return}if(!await et(e)){Aa(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=Dp(n.cols,120,jp),o=Dp(n.rows,32,Lp),s;try{s=Vv.spawn(M,["-S",Yt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Aa(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Yc(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Yc(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"&&Xv(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{}Yv(e)})}var zv=6,Jv=250;async function Yv(t,e=zv,n=Jv){for(let r=0;r<e;r++){if(await Gn(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var jp=20,Lp=6;function Xv(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=jp&&Number(e)>=Lp}function Dp(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Yc(t,e){try{t.send(JSON.stringify(e))}catch{}}function Aa(t,e){Yc(t,{type:"error",message:e})}function Fp(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 Fo from"node:fs";import Zv from"node:fs/promises";import $r from"node:path";import Qv from"node:os";var Up={"claude-opus-5":1e6,"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-5":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},tT=2e5,Xc=process.env.BOSTRAT_CLAUDE_PROJECTS||$r.join(Qv.homedir(),".claude","projects");function Zc(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Qc(t){let e=$r.join(Xc,Zc(t)),n;try{n=Fo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=$r.join(e,o);try{let{mtimeMs:a}=Fo.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function De(t,e){if(!e)return null;let n=$r.join(Xc,Zc(t),`${e}.jsonl`);return Fo.existsSync(n)?n:null}function Hp(t){if(!t)return[];let e=$r.join(Xc,Zc(t)),n;try{n=Fo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Fo.statSync($r.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Wt(t,e=131072){let n=await Zv.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 eT(t){let e=t.split(`
50
+ `);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 Uo(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=De(t,e),o=!0,!r)return null}else if(n){if(r=Qc(t),o=!1,!r)return null}else return null;let s;try{s=eT(await Wt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Up,s.model),i=Up[s.model]||tT;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 nT(t,e){return!t||!e?null:De(t,e)}function rT(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(`
51
+ `).trim():""}function oT(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 sT(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function tl(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 Wp(t,e){if(!t.uuid)return null;let n=tl(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 aT=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function el(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Wt(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
52
+ `)){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"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let h=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof h=="string"&&aT.test(h)&&s.set(m.id,h.split("/").pop())}if(sT(l.content)){let m=Wp(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=rT(l.content),d=u==="assistant"?oT(l.content):null,p=u==="user"?Wp(c,s):null;!f&&!d&&!p||o.push({role:u,text:f,tools:d||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var iT=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function qp(t,e,{turns:n=16}={}){let r=nT(t,e);if(!r)return"";let o=await el(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}:** ${iT(s.text||c,700)} ${i}`.trim()}).join(`
53
+
54
+ `):""}import m_ from"node:fs";import wn from"node:fs/promises";import gn from"node:path";import cT from"node:fs/promises";import{execFile as lT}from"node:child_process";import{promisify as uT}from"node:util";var nl=uT(lT),dT=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Kp=new Set,Gp=Promise.resolve(),rl=null;function zp(t){rl=typeof t=="function"?t:null}function fT(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function Jp({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||Kp.has(r)||(Kp.add(r),Gp=Gp.then(()=>pT({sessionName:t,from:e,to:r})).catch(()=>{}))}async function pT({sessionName:t,from:e,to:n}){await K({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await nl("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await cT.realpath(r),s=fT(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await nl("npm",["install","-g","@openai/codex"],{env:a,timeout:dT});let i=(await nl(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await K({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await Vp({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await K({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await Vp({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function Vp(t){if(rl)try{await rl(t)}catch{}}var mT=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Yp=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function hT(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
55
+ `);if(!Yp.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(mT);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"&&Yp.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 _a=/(^|\n)\s*›/,Qp=/^\s*Welcome to Codex\b/;function Or(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
56
+ `);for(let r=n.length-1;r>=0;r--)if(Qp.test(n[r]))return n.slice(r).join(`
57
+ `);return e}function wT(t){let e=String(t).trim();if(!e)return!1;let n=(e.match(/[A-Za-z]/g)||[]).length;return n>=3&&n/e.length>=.5}function gT(t){return/device code/i.test(t)?"works from the device you're reading this on":/sign in with chatgpt/i.test(t)?"opens a browser link on the node itself \u2014 can't finish from another device":""}var Xp=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function yT(t,e=""){let n=Or(t);if(_a.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
58
+ `),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Xp);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let p=r[d].trim();if(p){if(Xp.test(r[d])||/^Press enter/.test(p))break;f=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:f})}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&&!Qp.test(r[l])&&wT(u)&&i.push(u)}let c=i.join(" ").slice(0,400)||"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=>{let u=gT(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Zp=/Enter this one-time code/,bT=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function kT(t,e=""){let n=Or(t);if(_a.test(n)||!Zp.test(n))return null;let r=n.split(`
59
+ `),o=r.findIndex(i=>Zp.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(bT);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 xT(t,e=""){let n=Or(t);return _a.test(n)||!/open the following link to authenticate/.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 ST(t,e=""){let n=Or(t);return _a.test(n)||!/Paste or type your API key/.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:""}]}]}}var ol=/Run Everything|Add a follow-up|Plan, search, build anything/;function vT(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||ol.test(n))return null;let r=n.replace(/\r/g,"").split(`
60
+ `).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}var TT=/click this link to log in|Signing in with the browser/;function AT(t,e=""){let n=String(t);if(!TT.test(n)||ol.test(n))return null;let r=n.replace(/\r/g,"").split(`
61
+ `),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function _T(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||ol.test(n)?null:{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,promptId:"cursor:signin-qr",questions:[{question:"Cursor is showing a QR sign-in code in the Terminal tab \u2014 scan it from another device to log in. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function tm(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=kT(e,n)||xT(e,n)||ST(e,n)||yT(e,n);if(r)return r}if(t==="cursor"){let r=AT(e,n)||_T(e,n)||vT(e,n);if(r)return r}return hT(e,{provider:t,box:n})}function em(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 ET=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,sl=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Ho=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Mt={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,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/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},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/,auth:/Press any key to log in|click this link to log in|Scan this QR code to log in|Signing in with the browser/}},IT=/Trust this workspace|\[a\]\s*Trust/,RT=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function $T(t){let e=String(t||"");if(!Mt.codex.block.test(e))return null;let n=Or(e);if(!Mt.codex.block.test(n))return null;let r=n.match(RT);return{from:r?.[1]||null,to:r?.[2]||null}}function pn(t){return new Promise(e=>setTimeout(e,t))}var OT=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Ea=new Map;function nm(t,e){let n=Date.now(),r=OT();for(let[s,a]of Ea)n-a>=r&&Ea.delete(s);let o=`${t}::${e}`;return Ea.has(o)?!1:(Ea.set(o,n),!0)}var CT=68,MT=/^\s*[─━]{10,}\s*$/;function NT(t){let e=0;for(let n of String(t).split(`
62
+ `))MT.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function al(t,e){let n=Mt[t];if(!n?.busy)return{state:"idle",reason:null};if(cl(t,e))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(e))return{state:"idle",reason:null};if(n.busy.test(e))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(e))return{state:"busy",reason:null};let r=NT(e);return t==="claude"&&r>0&&r<CT?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function il(t,e){return al(t,e).state}var PT=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,DT=15;function om(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
+ `).slice(-DT).join(`
64
+ `);return PT.test(n)}function Wo(t,e){return il(t,e)==="busy"}function Ia(t,e){return il(t,e)!=="idle"}function Ra(t,e){let n=Mt[t];return n?.up?n.up.test(e):!0}function BT(t,e){let n=Mt[t];return n?cl(t,e)?!1:n.up.test(e)&&il(t,e)==="idle":!0}function cl(t,e){let n=Mt[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="codex"?!0:!n.up.test(e):!1:!1}async function rm(t,e,n){let r=Date.now()+n,o=0;for(await Gn(t).catch(()=>{});;){let s=await wt(t);if(BT(e,s)){await pn(Ho);return}if(e==="cursor"&&IT.test(s)&&!Mt.cursor.up.test(s)&&o<3){if(!nm(t,"cursor-trust")){await pn(sl);continue}o+=1,await K({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await $t(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await pn(Ho);continue}if(e==="codex"&&o<3){let a=$T(s);if(a){if(!nm(t,`codex-update:${a.from}->${a.to}`)){await pn(sl);continue}o+=1,await K({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await $t(t,"3",{enter:!1,kind:"codex-update-skip"}),Jp({sessionName:t,from:a.from,to:a.to}),await pn(Ho);continue}}if(Date.now()>r){if(cl(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Mt.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==="cursor"&&Mt.cursor.auth.test(s)?a="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":e==="claude"&&Mt.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":e==="claude"&&Mt.claude.block.test(s)&&(a='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),K({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await Ir(t)){let a=`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw K({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",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.`),K({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Mt[e]?.block?.test(s),paneUp:!!Mt[e]?.up?.test(s),paneBusy:!!Mt[e]?.busy?.test(s)}).catch(()=>{}),await pn(Ho);return}await pn(sl)}}function sm({agent:t,agentCmd:e}={}){let n=e?un(e):un(t||"");return{label:n,realClaude:e?qn(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 $a({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=sm({agent:n,agentCmd:r});return!e||!o&&!s?null:mn({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function mn({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=ET()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=sm({agent:r,agentCmd:o});if(i&&t&&await sn(t),c&&t&&await br(t),l&&t&&await kr(t),(i||c||l)&&n){await rm(n,a,s);return}let u=Mt[r]?r:Mt[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await rm(n,u,s);return}await pn(Ho)}var jT=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),LT=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function Be(t){return t==="codex"?{enter:!0,submitDelayMs:jT}:t==="claude"?{enter:!0,submitDelayMs:LT}:{enter:!0}}import Ca from"node:fs/promises";import dm from"node:path";var FT=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],UT={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 Vn(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of FT){let o=0;for(let s of UT[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var HT=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),WT=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function am(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))HT.has(r)&&(e+=o||0),WT.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function qo(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 im=1.5,cm=.75,qT=new Set("a an and are as at be been but by for from had has have he her his i if in into is it its of on or our so than that the their them then there these they this those to was were will with would you your we".split(" "));function um(t){let e=qo(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||qT.has(r)||n.push(r);return n}function KT(t){let e=[];for(let n of t||[]){let r=n.payload||{},o=String(r.text||"").trim();if(n.type==="turn.user"){if(n.source==="hook"||!o)continue;e.push(lm(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(lm(n,"assistant",o))}}return e}function lm(t,e,n){let r=Vn(n);return{id:t.id,seq:t.seq,ts:t.payload?.ts??t.ts,role:e,boxId:t.boxId??null,provider:t.provider??null,text:n,tags:r?[r.category]:[]}}function Ko(t){let e=KT(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=um(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=e.length;return{chunks:e,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function Oa(t,e,n={}){let{k:r=8,role:o=null,tag:s=null,boxId:a=null}=n;if(!t||!t.N)return[];let i=[...new Set(um(e))];if(!i.length)return[];let c=new Map;for(let u of i){let f=t.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(t.N-d+.5)/(d+.5));for(let[m,h]of f){let g=t.lengths[m],k=h+im*(1-cm+cm*(g/(t.avgdl||1))),S=p*(h*(im+1)/k);c.set(m,(c.get(m)||0)+S)}}let l=[];for(let[u,f]of c){let d=t.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var fm="Relevant prior discussion (verbatim, retrieved";async function GT(t){let e=dm.join(t,".bostrat","events"),n;try{n=await Ca.readdir(e)}catch{return Ko([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Ca.readFile(dm.join(e,o),"utf8");for(let a of s.split(`
65
+ `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Ko(r)}function VT(t){let e=String(t||""),n=a=>{let i=e.match(a);return i?i[1].trim():""},r=n(/#+\s*Objective\s*\n([\s\S]*?)(?:\n#+\s|$)/i),o=n(/#+\s*Next steps?[^\n]*\n([\s\S]*?)(?:\n#+\s|$)/i);return`${r}
66
+ ${o}`.trim()||e.slice(0,2e3)}function zT(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Oa(t,e,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),f=u.length>800?u.slice(0,800)+"\u2026":u,d=`- **${l}:** ${f}`;if(i+d.length>r||(a.push(d),i+=d.length,a.length>=n))break}return a.length?["",`## ${fm}, not a summary)`,"_Auto-selected from the Track's event log by lexical relevance to the Objective / Next steps. Verbatim excerpts, not re-summarization (docs/memory-segmentation-indexing-scope.md \xA71.3)._","",...a,""].join(`
67
+ `):""}async function pm(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Ca.readFile(e,"utf8");if(a.includes(fm))return{appended:!1,reason:"already-enriched"};let i=await GT(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=zT(i,VT(a),{k:r,maxChars:o,exclude:s});return c?(await Ca.appendFile(e,`
68
68
  `+c),{appended:!0,chars:c.length,chunks:c.split(`
69
- - `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}import ol from"node:fs/promises";import $r from"node:path";import RT from"node:os";import{EventEmitter as $T}from"node:events";import{randomUUID as OT}from"node:crypto";var MT=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","context.usage","cost.recorded","memory.seed","handoff.artifact","code.pushed","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","flow.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","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","changelog.published","stall.detected","error"]),CT=new Set(["user","agent","orchestrator","system","peer"]),NT=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Ht=new $T;Ht.setMaxListeners(0);var sl=process.env.BOSTRAT_STATE_DIR||$r.join(process.env.HOME||RT.tmpdir(),".bostrat");function Ra(t){return t?.cwd?$r.join(t.cwd,".bostrat","events"):$r.join(sl,"events")}var sm=(t,e)=>$r.join(t,`${e}.ndjson`),Ia=new Map;async function PT(t,e){if(!Ia.has(e)){let o=(await B(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);Ia.set(e,o)}let n=Ia.get(e)+1;return Ia.set(e,n),n}function DT(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 $(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!MT.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!CT.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!NT.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?$r.join(sl,"events"):Ra(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=DT(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await PT(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||OT(),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 ol.mkdir(r,{recursive:!0}),await ol.appendFile(sm(r,n),JSON.stringify(i)+`
70
- `),Ht.emit("event",i),Ht.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"?$r.join(sl,"events"):Ra(t));let o;try{o=await ol.readFile(sm(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
71
- `))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function am(t,e){let n=`track:${t}`;return Ht.on(n,e),()=>Ht.off(n,e)}function BT(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===`
72
- `){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function Wo(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 qo(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 Mr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Mt=t=>t&&!/[$`]/.test(t)?t:null,gt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Or=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},lm=t=>t.startsWith("-"),im=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function ut(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(lm(o)){e.has(o)&&r++;continue}n.push(o)}return n}var jT={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 ${gt(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"},LT={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 um(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 FT(t){let e=um(t),n=Mt(ut(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${gt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${gt(n)}`:t.includes("-i")&&n?`Edit ${gt(n)}`:null}function al(t){if(t.includes("--files")){let s=Mt(ut(t,im)[0]);return s&&s!=="."?`List files in ${Or(s)}`:"List files"}let e=ut(t,im),n=e[0];if(!n)return null;let r=Mt(e.slice(1).find(s=>s!==".")),o=Mr(n,40);return r?`Search for \u201C${o}\u201D in ${Or(r)}`:`Search for \u201C${o}\u201D`}function il(t){let e=ut(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 cl(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=ut(t)[0];return n?`Run ${gt(n)}`:null}function UT(t){let e=ut(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 cm(t){return t.find(e=>/^https?:\/\//.test(e))||null}var HT={git:t=>{let e=ut(t,new Set(["-C","-c"])),n=jT[e[0]];return n?n(e,t):null},gh:t=>{let e=ut(t),n=LT[e[0]];return n?n(e):null},sed:FT,rg:al,grep:al,egrep:al,nl:t=>{let e=Mt(ut(t)[0]);return e?`Read ${gt(e)}`:null},cat:t=>{let e=Mt(ut(t)[0]);return e?`Read ${gt(e)}`:null},head:t=>{let e=Mt(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${gt(e)}`:null},tail:t=>{let e=Mt(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${gt(e)}`:null},wc:t=>{let e=Mt(ut(t)[0]);return e?`Count lines in ${gt(e)}`:"Count input lines"},ls:t=>{let e=Mt(ut(t)[0]);return e&&e!=="."?`List files in ${Or(e)}`:"List files"},find:t=>Mt(t[1])&&!lm(t[1])&&t[1]!=="."?`Find files in ${Or(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Mt(ut(t)[0]);return e?`Create directory ${Or(e)}`:null},touch:t=>{let e=Mt(ut(t)[0]);return e?`Create ${gt(e)}`:null},rm:t=>{let e=Mt(ut(t)[0]);return e?`Delete ${Or(e)}`:null},mv:t=>{let e=Mt(ut(t)[0]);return e?`Move ${gt(e)}`:null},cp:t=>{let e=Mt(ut(t)[0]);return e?`Copy ${gt(e)}`:null},chmod:t=>{let e=Mt(ut(t)[1]);return e?`Change permissions on ${gt(e)}`:null},diff:t=>{let e=ut(t);return e.length>=2?`Compare ${gt(e[0])} and ${gt(e[1])}`:null},curl:t=>{let e=cm(t);return e?`Fetch ${Mr(e,56)}`:"Fetch a URL"},wget:t=>{let e=cm(t);return e?`Fetch ${Mr(e,56)}`:"Fetch a URL"},npm:il,pnpm:il,yarn:il,npx:t=>{let e=ut(t)[0];return e?`Run ${e}`:null},node:t=>cl(t,"Node"),python:t=>cl(t,"Python"),python3:t=>cl(t,"Python"),make:t=>{let e=ut(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:UT,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=ut(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=ut(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=ut(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},WT=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function qT(t){let e=qo(Wo(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(gt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return ll(e[2]);let n=gt(e[0]);if(WT.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=HT[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function ll(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
73
- `,n);i!==-1&&(e=e.slice(0,i))}let r=BT(e);for(let i=0;i<r.length-1;i++){let c=qo(Wo(r[i])),l=qo(Wo(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(gt(c[0]))&&gt(l[0])==="sed"){let u=um(l),f=ut(c)[0];if(u&&f)return Mr(u.from===u.to?`Read line ${u.from} of ${gt(f)}`:`Read lines ${u.from}\u2013${u.to} of ${gt(f)}`,88)}}let o=[];for(let i of r){let c=qT(i);c&&o.push({d:c,head:gt(qo(Wo(i))[0]||""),sub:ut(qo(Wo(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":Mr(s?s.d:o[0].d,88)}var dl=new Set(["Bash"]);function Cr(t){let e=t.split(`
74
- `).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function mn(t,e=""){let n=e.trim();return n||ll(t)||Cr(t)}function ul(t,e){if(t){if(Array.isArray(t)){for(let n of t)ul(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&dl.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))ul(n,e)}}}async function dm(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Ut(t,e)}catch{return[]}let o=[];for(let s of r.split(`
75
- `)){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(ul(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:mn(f.command,f.description),ts:u})}return o.slice(-n)}function fm(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,h)=>h>=s&&(m.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let p=d.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:p,ts:Math.max(...p.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}import pm from"node:fs";import pl from"node:fs/promises";import fl from"node:path";import KT from"node:os";var GT=process.env.BOSTRAT_CODEX_SESSIONS||fl.join(process.env.CODEX_HOME||fl.join(KT.homedir(),".codex"),"sessions");async function mm(t){try{return await pl.realpath(t)}catch{return t}}function VT(t=GT){let e=[],n=r=>{let o;try{o=pm.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=fl.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:pm.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function zT(t,e=1048576){let n;try{n=await pl.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(`
76
- `);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function JT(t){let e=await zT(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 Vn(t,e){if(!t&&!e)return null;let n=VT();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await mm(t);for(let o of n){let s=await JT(o.file);if(s&&(s===t||await mm(s)===r))return o}return null}var XT=new Set(["output_text","text"]),YT=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&XT.has(e.type)).map(e=>e.text).join(`
77
- `).trim():"";async function hm(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Vn(t,e);if(!o)return[];let s;try{s=await Ut(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
78
- `)){let p=d.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}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 v=m.payload?.type;v==="task_started"?(l=!0,u=!1,f=m.payload?.turn_id||null):v==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:hl,sourceId:m.payload?.turn_id||f||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let h=m.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,ml.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let y=YT(h.content),k=c.length?c.slice():null;c=[],!(!y&&!k)&&a.push({role:"assistant",text:y,tools:k||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}var Ko=new Set(["exec_command","shell","exec"]),ml=new Set(["wait","write_stdin"]),hl="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 ZT(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 QT(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+=`
79
- `;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 tA(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=QT(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function wm(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Vn(t,e);if(!o)return[];let s;try{s=await Ut(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
80
- `)){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 f=[];if(u.type==="function_call"&&Ko.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=ZT(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=tA(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,m)=>{a.push({id:d?m?`${d}:${m}`:d:null,tool:u.name,command:p,description:"",summary:mn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function gm(t,e){let n=await Vn(t,e);if(!n)return null;try{return Math.round((await pl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var ym=/^\s*─{8,}\s*$/,eA=/^❯[  ]?(.*)$/;function Go(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
81
- `).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(ym.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(ym.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(eA);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 vt(t){return String(t||"").replace(/\s+/g," ").trim()}function bm(t,e,{slackMs:n=2e4}={}){let r=vt(e?.text);return!r||typeof e?.at!="number"?!1:(Array.isArray(t)?t:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=e.at-n&&vt(o.text).includes(r))}function km(t,e,n,{busy:r=!1}={}){let o=Go(t,n);return o&&wl(e,o)?"stuck":r||o?"submitted":"unknown"}function wl(t,e){let n=vt(t),r=vt(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var nA=/^This session is being continued from a previous conversation that ran out of context\./,vm=["Primary Request and Intent","Key Technical Concepts","Files and Code Sections","Errors and Fixes","Problem Solving","All user messages","Pending Tasks","Current Work","Optional Next Step"],gl=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${vm.join("|")})[:*\t ]*$`,"gim"),rA=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,oA=420,sA=[{h:"What we were doing",from:["Primary Request and Intent"]},{h:"Key decisions",from:["Key Technical Concepts"]},{h:"Where it got to",from:["Current Work","Problem Solving"]},{h:"Left to do",from:["Pending Tasks","Optional Next Step"]}],aA=116,yl=168;function iA(t){return nA.test(String(t||"").trimStart())}function Sm(t){let e=String(t||"");if(!iA(e.trimStart()))return null;let n=e.replace(rA,"").trim(),r=cA(n),o=[];for(let u of sA){let f=u.from.map(d=>Nr(r.get(d))).find(d=>d&&!xm(d));f&&o.push({h:u.h,text:Vo(f,oA)})}let s=Nr(r.get("Current Work"))||Nr(r.get("Primary Request and Intent"))||uA(n),a=Oa(s),i=Vo($a(bl(a[0]||"")),aA)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Vo($a(bl(l)),yl));for(let u of["Pending Tasks","Optional Next Step"]){let f=Oa(Nr(r.get(u)))[0]||"";if(!(!f||xm(f))){c.push(Vo(`Left to do: ${$a(bl(f))}`,yl));break}}if(!c.length){let u=Oa(Nr(r.get("Problem Solving"))||"")[0];u&&c.push(Vo($a(u),yl))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:e.length}}function cA(t){let e=new Map,n=[];gl.lastIndex=0;for(let r=gl.exec(t);r;r=gl.exec(t))n.push({name:lA(r[1]),start:r.index,end:r.index+r[0].length});for(let r=0;r<n.length;r++){let o=n[r+1]?.start??t.length;e.has(n[r].name)||e.set(n[r].name,t.slice(n[r].end,o).trim())}return e}function lA(t){return vm.find(n=>n.toLowerCase()===t.toLowerCase())||t}function Nr(t){return t?String(t).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
82
- `).map(e=>e.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(e=>e&&!/^#{1,6}[ \t]/.test(e)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function uA(t){let e=t.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Nr(e.split(/\n{2,}/).slice(0,2).join(" "))}function Oa(t){let e=String(t||"").trim();if(!e)return[];let r=e.replace(/\b([A-Za-z0-9_-]+)\.(js|ts|tsx|mjs|json|md|css|jsonl|sh|yml|yaml|py|html)\b/g,"$1<DOT>$2").replace(/\b(e\.g|i\.e|etc|vs|approx|no|fig|Dr|Mr|Ms|St)\./gi,"$1<DOT>").split(/(?<=[.!?])["')\]]?\s+(?=[A-Z"“'(\[]|\d)/).map(s=>s.replace(/<DOT>/g,".").trim()).filter(Boolean),o=[];for(let s of r){let a=o[o.length-1];a&&(a.match(/["“”]/g)?.length??0)%2===1?o[o.length-1]=`${a} ${s}`:o.push(s)}return o}function bl(t){return String(t||"").replace(/^(?:Immediately before (?:this|the) summary(?: request)?[,:]?\s*|At the (?:time|point) of (?:this )?compaction[,:]?\s*|Currently[,:]?\s*|The (?:most recent|immediately preceding|current) work (?:was|is)\s*)/i,"").replace(/^(?:I(?:'m|'d| am| was| had| have)?\s+(?:currently\s+|just\s+)?(?:been\s+)?(?:working on|in the middle of|completing|finishing)\s+|The user(?:'s)?\s+(?:most recent\s+)?(?:request|ask) (?:was|is)\s+(?:to\s+)?)/i,"").replace(/^I(?:'m|'d| am| was| had| have)?\s+(?:just\s+|then\s+)?(?=(?:[a-z]+(?:ed|ing)|ran|wrote|built|made|began|found|left|sent|took|went|did|got|kept|held|brought|caught|chose|came|saw|said|gave|told|set|put|read)\b)/,"").replace(/^["“]|["”]$/g,"").replace(/^(\w)/,e=>e.toUpperCase()).trim()}function $a(t){return String(t||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,e=>`\u2026/${e.split("/").slice(-2).join("/")}`)}function xm(t){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(t||"").trim())}function Vo(t,e){let n=String(t||"").trim();if(n.length<=e)return n;let r=Oa(n),o="";for(let i of r){let c=o?`${o} ${i}`:i;if(c.length>e)break;o=c}if(o.length>=Math.min(60,e*.4))return o;let s=n.slice(0,e),a=s.lastIndexOf(" ");return`${(a>e*.6?s.slice(0,a):s).replace(/[,;:.\s]+$/,"")}\u2026`}var dA=/^Base directory for this skill:\s*(\S+)/,fA=/^Run the "([^"]+)" workflow\./,pA=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,mA=/^<local-command-(?:stdout|caveat)>/i,hA=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,wA=/^Continue from where you left off\.$/,gA=/^<task-notification>/i,yA=/<summary>([\s\S]*?)<\/summary>/i,bA=/<task-id>([\s\S]*?)<\/task-id>/i,kA=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,xA=/<status>([\s\S]*?)<\/status>/i,vA=/<result>([\s\S]*?)(?:<\/result>|$)/i,Tm=600,kl=new Set(["model","clear","compact","login","exit","quit"]);function Am(t){return kl.has(String(t||"").replace(/^\//,"").toLowerCase())}var SA=/^\/([a-z0-9:_-]+)$/i;function Ma(t){let e=String(t||"").trim();if(!e)return null;let n=Sm(e);if(n)return n;let r=e.match(dA);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=e.match(fA);if(o)return{kind:"skill",name:o[1]};let s=e.match(pA);if(s){let i=s[1].toLowerCase();return kl.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=e.match(SA);if(a&&kl.has(a[1].toLowerCase()))return{kind:"suppress"};if(mA.test(e))return{kind:"suppress"};if(wA.test(e))return{kind:"suppress"};if(hA.test(e))return{kind:"suppress"};if(gA.test(e)){let i=l=>TA(e.match(l)?.[1].trim()||"")||null,c=i(vA);return{kind:"task",summary:i(yA)||"",taskId:i(bA),toolUseId:i(kA),status:i(xA)?.toLowerCase()||null,resultHead:c?c.length>Tm?`${c.slice(0,Tm).trimEnd()}\u2026`:c:null}}return null}function TA(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function _m(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":t?.kind==="resume-summary"?t.title?`Context resumed from summary: ${t.title}`:"Context resumed from summary":""}function Em(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":t?.kind==="resume-summary"?"context-resumed":"command-run"}import AA from"node:crypto";var xl=new Set(["cursor","codex"]),Rm=4,_A=4,EA=2,IA=24,Im=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,zo=t=>typeof t=="string"?t.trim():"";function RA(t){let e=Array.isArray(t)?t:Array.isArray(t?.questions)?t.questions:null;if(!e||!e.length)return null;let n=[];for(let r of e.slice(0,Rm)){let o=zo(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,_A)){let c=zo(i?.label);if(!c)return null;let l=zo(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<EA)return null;n.push({question:o,header:zo(r?.header).slice(0,IA),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function vl(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;Im.lastIndex=0;let e=null;for(let o;o=Im.exec(t);)e=o;if(!e||e.index+e[0].length<t.trimEnd().length)return null;let n;try{n=JSON.parse(e[2])}catch{return null}let r=RA(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function Sl(t,e,n){let r=AA.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Tl(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=Rm}={}){if(!Array.isArray(t)||!e||!xl.has(n))return null;let s=null;for(let l=t.length-1;l>=0;l--){let u=t[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==e||s.agent!==n)return null;let a=s.ask?.questions||vl(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Sl(n,s.sourceId||s.id,a);return typeof r=="function"&&r(c)?null:{provider:n,box:e,promptId:c,groupId:c,answerMode:"say",source:"transcript",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function $m(t,e,n){let r=[];(t?.questions||[]).forEach((a,i)=>{let c=(e?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=zo(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
69
+ - `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}import ll from"node:fs/promises";import Cr from"node:path";import JT from"node:os";import{EventEmitter as YT}from"node:events";import{randomUUID as XT}from"node:crypto";var ZT=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","handoff.artifact","code.pushed","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","flow.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","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","changelog.published","stall.detected","error"]),QT=new Set(["user","agent","orchestrator","system","peer"]),tA=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),qt=new YT;qt.setMaxListeners(0);var ul=process.env.BOSTRAT_STATE_DIR||Cr.join(process.env.HOME||JT.tmpdir(),".bostrat");function Na(t){return t?.cwd?Cr.join(t.cwd,".bostrat","events"):Cr.join(ul,"events")}var mm=(t,e)=>Cr.join(t,`${e}.ndjson`),Ma=new Map;async function eA(t,e){if(!Ma.has(e)){let o=(await P(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);Ma.set(e,o)}let n=Ma.get(e)+1;return Ma.set(e,n),n}function nA(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 I(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!ZT.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!QT.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!tA.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Cr.join(ul,"events"):Na(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=nA(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await eA(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||XT(),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 ll.mkdir(r,{recursive:!0}),await ll.appendFile(mm(r,n),JSON.stringify(i)+`
70
+ `),qt.emit("event",i),qt.emit(`track:${n}`,i),i}async function P(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Cr.join(ul,"events"):Na(t));let o;try{o=await ll.readFile(mm(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
71
+ `))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function hm(t,e){let n=`track:${t}`;return qt.on(n,e),()=>qt.off(n,e)}function rA(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===`
72
+ `){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function Go(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 Vo(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 Nr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Nt=t=>t&&!/[$`]/.test(t)?t:null,yt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Mr=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},ym=t=>t.startsWith("-"),wm=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function ut(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(ym(o)){e.has(o)&&r++;continue}n.push(o)}return n}var oA={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 ${yt(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"},sA={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 bm(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 aA(t){let e=bm(t),n=Nt(ut(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${yt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${yt(n)}`:t.includes("-i")&&n?`Edit ${yt(n)}`:null}function dl(t){if(t.includes("--files")){let s=Nt(ut(t,wm)[0]);return s&&s!=="."?`List files in ${Mr(s)}`:"List files"}let e=ut(t,wm),n=e[0];if(!n)return null;let r=Nt(e.slice(1).find(s=>s!==".")),o=Nr(n,40);return r?`Search for \u201C${o}\u201D in ${Mr(r)}`:`Search for \u201C${o}\u201D`}function fl(t){let e=ut(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 pl(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=ut(t)[0];return n?`Run ${yt(n)}`:null}function iA(t){let e=ut(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 gm(t){return t.find(e=>/^https?:\/\//.test(e))||null}var cA={git:t=>{let e=ut(t,new Set(["-C","-c"])),n=oA[e[0]];return n?n(e,t):null},gh:t=>{let e=ut(t),n=sA[e[0]];return n?n(e):null},sed:aA,rg:dl,grep:dl,egrep:dl,nl:t=>{let e=Nt(ut(t)[0]);return e?`Read ${yt(e)}`:null},cat:t=>{let e=Nt(ut(t)[0]);return e?`Read ${yt(e)}`:null},head:t=>{let e=Nt(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${yt(e)}`:null},tail:t=>{let e=Nt(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${yt(e)}`:null},wc:t=>{let e=Nt(ut(t)[0]);return e?`Count lines in ${yt(e)}`:"Count input lines"},ls:t=>{let e=Nt(ut(t)[0]);return e&&e!=="."?`List files in ${Mr(e)}`:"List files"},find:t=>Nt(t[1])&&!ym(t[1])&&t[1]!=="."?`Find files in ${Mr(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Nt(ut(t)[0]);return e?`Create directory ${Mr(e)}`:null},touch:t=>{let e=Nt(ut(t)[0]);return e?`Create ${yt(e)}`:null},rm:t=>{let e=Nt(ut(t)[0]);return e?`Delete ${Mr(e)}`:null},mv:t=>{let e=Nt(ut(t)[0]);return e?`Move ${yt(e)}`:null},cp:t=>{let e=Nt(ut(t)[0]);return e?`Copy ${yt(e)}`:null},chmod:t=>{let e=Nt(ut(t)[1]);return e?`Change permissions on ${yt(e)}`:null},diff:t=>{let e=ut(t);return e.length>=2?`Compare ${yt(e[0])} and ${yt(e[1])}`:null},curl:t=>{let e=gm(t);return e?`Fetch ${Nr(e,56)}`:"Fetch a URL"},wget:t=>{let e=gm(t);return e?`Fetch ${Nr(e,56)}`:"Fetch a URL"},npm:fl,pnpm:fl,yarn:fl,npx:t=>{let e=ut(t)[0];return e?`Run ${e}`:null},node:t=>pl(t,"Node"),python:t=>pl(t,"Python"),python3:t=>pl(t,"Python"),make:t=>{let e=ut(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:iA,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=ut(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=ut(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=ut(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},lA=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function uA(t){let e=Vo(Go(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(yt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return ml(e[2]);let n=yt(e[0]);if(lA.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=cA[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function ml(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
73
+ `,n);i!==-1&&(e=e.slice(0,i))}let r=rA(e);for(let i=0;i<r.length-1;i++){let c=Vo(Go(r[i])),l=Vo(Go(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(yt(c[0]))&&yt(l[0])==="sed"){let u=bm(l),f=ut(c)[0];if(u&&f)return Nr(u.from===u.to?`Read line ${u.from} of ${yt(f)}`:`Read lines ${u.from}\u2013${u.to} of ${yt(f)}`,88)}}let o=[];for(let i of r){let c=uA(i);c&&o.push({d:c,head:yt(Vo(Go(i))[0]||""),sub:ut(Vo(Go(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":Nr(s?s.d:o[0].d,88)}var wl=new Set(["Bash"]);function Pr(t){let e=t.split(`
74
+ `).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function hn(t,e=""){let n=e.trim();return n||ml(t)||Pr(t)}function hl(t,e){if(t){if(Array.isArray(t)){for(let n of t)hl(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&wl.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))hl(n,e)}}}async function km(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Wt(t,e)}catch{return[]}let o=[];for(let s of r.split(`
75
+ `)){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(hl(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:hn(f.command,f.description),ts:u})}return o.slice(-n)}function xm(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,h)=>h>=s&&(m.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let p=d.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:p,ts:Math.max(...p.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}import Sm from"node:fs";import yl from"node:fs/promises";import gl from"node:path";import dA from"node:os";var fA=process.env.BOSTRAT_CODEX_SESSIONS||gl.join(process.env.CODEX_HOME||gl.join(dA.homedir(),".codex"),"sessions");async function vm(t){try{return await yl.realpath(t)}catch{return t}}function pA(t=fA){let e=[],n=r=>{let o;try{o=Sm.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=gl.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:Sm.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function mA(t,e=1048576){let n;try{n=await yl.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(`
76
+ `);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function hA(t){let e=await mA(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 zn(t,e){if(!t&&!e)return null;let n=pA();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await vm(t);for(let o of n){let s=await hA(o.file);if(s&&(s===t||await vm(s)===r))return o}return null}var wA=new Set(["output_text","text"]),gA=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&wA.has(e.type)).map(e=>e.text).join(`
77
+ `).trim():"";async function Tm(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await zn(t,e);if(!o)return[];let s;try{s=await Wt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
78
+ `)){let p=d.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}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,f=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:kl,sourceId:m.payload?.turn_id||f||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let h=m.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,bl.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let g=gA(h.content),k=c.length?c.slice():null;c=[],!(!g&&!k)&&a.push({role:"assistant",text:g,tools:k||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}var zo=new Set(["exec_command","shell","exec"]),bl=new Set(["wait","write_stdin"]),kl="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 yA(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 bA(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+=`
79
+ `;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 kA(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=bA(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Am(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await zn(t,e);if(!o)return[];let s;try{s=await Wt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
80
+ `)){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 f=[];if(u.type==="function_call"&&zo.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=yA(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=kA(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,m)=>{a.push({id:d?m?`${d}:${m}`:d:null,tool:u.name,command:p,description:"",summary:hn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function _m(t,e){let n=await zn(t,e);if(!n)return null;try{return Math.round((await yl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var Em=/^\s*─{8,}\s*$/,xA=/^❯[  ]?(.*)$/;function Jo(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
81
+ `).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Em.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Em.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(xA);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 Tt(t){return String(t||"").replace(/\s+/g," ").trim()}function Im(t,e,{slackMs:n=2e4}={}){let r=Tt(e?.text);return!r||typeof e?.at!="number"?!1:(Array.isArray(t)?t:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=e.at-n&&Tt(o.text).includes(r))}function Rm(t,e,n,{busy:r=!1}={}){let o=Jo(t,n);return o&&xl(e,o)?"stuck":r||o?"submitted":"unknown"}function xl(t,e){let n=Tt(t),r=Tt(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var SA=/^This session is being continued from a previous conversation that ran out of context\./,Om=["Primary Request and Intent","Key Technical Concepts","Files and Code Sections","Errors and Fixes","Problem Solving","All user messages","Pending Tasks","Current Work","Optional Next Step"],Sl=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Om.join("|")})[:*\t ]*$`,"gim"),vA=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,TA=420,AA=[{h:"What we were doing",from:["Primary Request and Intent"]},{h:"Key decisions",from:["Key Technical Concepts"]},{h:"Where it got to",from:["Current Work","Problem Solving"]},{h:"Left to do",from:["Pending Tasks","Optional Next Step"]}],_A=116,vl=168;function EA(t){return SA.test(String(t||"").trimStart())}function Cm(t){let e=String(t||"");if(!EA(e.trimStart()))return null;let n=e.replace(vA,"").trim(),r=IA(n),o=[];for(let u of AA){let f=u.from.map(d=>Dr(r.get(d))).find(d=>d&&!$m(d));f&&o.push({h:u.h,text:Yo(f,TA)})}let s=Dr(r.get("Current Work"))||Dr(r.get("Primary Request and Intent"))||$A(n),a=Da(s),i=Yo(Pa(Tl(a[0]||"")),_A)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Yo(Pa(Tl(l)),vl));for(let u of["Pending Tasks","Optional Next Step"]){let f=Da(Dr(r.get(u)))[0]||"";if(!(!f||$m(f))){c.push(Yo(`Left to do: ${Pa(Tl(f))}`,vl));break}}if(!c.length){let u=Da(Dr(r.get("Problem Solving"))||"")[0];u&&c.push(Yo(Pa(u),vl))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:e.length}}function IA(t){let e=new Map,n=[];Sl.lastIndex=0;for(let r=Sl.exec(t);r;r=Sl.exec(t))n.push({name:RA(r[1]),start:r.index,end:r.index+r[0].length});for(let r=0;r<n.length;r++){let o=n[r+1]?.start??t.length;e.has(n[r].name)||e.set(n[r].name,t.slice(n[r].end,o).trim())}return e}function RA(t){return Om.find(n=>n.toLowerCase()===t.toLowerCase())||t}function Dr(t){return t?String(t).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
82
+ `).map(e=>e.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(e=>e&&!/^#{1,6}[ \t]/.test(e)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function $A(t){let e=t.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Dr(e.split(/\n{2,}/).slice(0,2).join(" "))}function Da(t){let e=String(t||"").trim();if(!e)return[];let r=e.replace(/\b([A-Za-z0-9_-]+)\.(js|ts|tsx|mjs|json|md|css|jsonl|sh|yml|yaml|py|html)\b/g,"$1<DOT>$2").replace(/\b(e\.g|i\.e|etc|vs|approx|no|fig|Dr|Mr|Ms|St)\./gi,"$1<DOT>").split(/(?<=[.!?])["')\]]?\s+(?=[A-Z"“'(\[]|\d)/).map(s=>s.replace(/<DOT>/g,".").trim()).filter(Boolean),o=[];for(let s of r){let a=o[o.length-1];a&&(a.match(/["“”]/g)?.length??0)%2===1?o[o.length-1]=`${a} ${s}`:o.push(s)}return o}function Tl(t){return String(t||"").replace(/^(?:Immediately before (?:this|the) summary(?: request)?[,:]?\s*|At the (?:time|point) of (?:this )?compaction[,:]?\s*|Currently[,:]?\s*|The (?:most recent|immediately preceding|current) work (?:was|is)\s*)/i,"").replace(/^(?:I(?:'m|'d| am| was| had| have)?\s+(?:currently\s+|just\s+)?(?:been\s+)?(?:working on|in the middle of|completing|finishing)\s+|The user(?:'s)?\s+(?:most recent\s+)?(?:request|ask) (?:was|is)\s+(?:to\s+)?)/i,"").replace(/^I(?:'m|'d| am| was| had| have)?\s+(?:just\s+|then\s+)?(?=(?:[a-z]+(?:ed|ing)|ran|wrote|built|made|began|found|left|sent|took|went|did|got|kept|held|brought|caught|chose|came|saw|said|gave|told|set|put|read)\b)/,"").replace(/^["“]|["”]$/g,"").replace(/^(\w)/,e=>e.toUpperCase()).trim()}function Pa(t){return String(t||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,e=>`\u2026/${e.split("/").slice(-2).join("/")}`)}function $m(t){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(t||"").trim())}function Yo(t,e){let n=String(t||"").trim();if(n.length<=e)return n;let r=Da(n),o="";for(let i of r){let c=o?`${o} ${i}`:i;if(c.length>e)break;o=c}if(o.length>=Math.min(60,e*.4))return o;let s=n.slice(0,e),a=s.lastIndexOf(" ");return`${(a>e*.6?s.slice(0,a):s).replace(/[,;:.\s]+$/,"")}\u2026`}var OA=/^Base directory for this skill:\s*(\S+)/,CA=/^Run the "([^"]+)" workflow\./,MA=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,NA=/^<local-command-(?:stdout|caveat)>/i,PA=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,DA=/^Continue from where you left off\.$/,BA=/^<task-notification>/i,jA=/<summary>([\s\S]*?)<\/summary>/i,LA=/<task-id>([\s\S]*?)<\/task-id>/i,FA=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,UA=/<status>([\s\S]*?)<\/status>/i,HA=/<result>([\s\S]*?)(?:<\/result>|$)/i,Mm=600,Al=new Set(["model","clear","compact","login","exit","quit"]);function Nm(t){return Al.has(String(t||"").replace(/^\//,"").toLowerCase())}var WA=/^\/([a-z0-9:_-]+)$/i;function Ba(t){let e=String(t||"").trim();if(!e)return null;let n=Cm(e);if(n)return n;let r=e.match(OA);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=e.match(CA);if(o)return{kind:"skill",name:o[1]};let s=e.match(MA);if(s){let i=s[1].toLowerCase();return Al.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=e.match(WA);if(a&&Al.has(a[1].toLowerCase()))return{kind:"suppress"};if(NA.test(e))return{kind:"suppress"};if(DA.test(e))return{kind:"suppress"};if(PA.test(e))return{kind:"suppress"};if(BA.test(e)){let i=l=>qA(e.match(l)?.[1].trim()||"")||null,c=i(HA);return{kind:"task",summary:i(jA)||"",taskId:i(LA),toolUseId:i(FA),status:i(UA)?.toLowerCase()||null,resultHead:c?c.length>Mm?`${c.slice(0,Mm).trimEnd()}\u2026`:c:null}}return null}function qA(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function Pm(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":t?.kind==="resume-summary"?t.title?`Context resumed from summary: ${t.title}`:"Context resumed from summary":""}function Dm(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":t?.kind==="resume-summary"?"context-resumed":"command-run"}import KA from"node:crypto";var _l=new Set(["cursor","codex"]),jm=4,GA=4,VA=2,zA=24,Bm=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Xo=t=>typeof t=="string"?t.trim():"";function JA(t){let e=Array.isArray(t)?t:Array.isArray(t?.questions)?t.questions:null;if(!e||!e.length)return null;let n=[];for(let r of e.slice(0,jm)){let o=Xo(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,GA)){let c=Xo(i?.label);if(!c)return null;let l=Xo(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<VA)return null;n.push({question:o,header:Xo(r?.header).slice(0,zA),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function El(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;Bm.lastIndex=0;let e=null;for(let o;o=Bm.exec(t);)e=o;if(!e||e.index+e[0].length<t.trimEnd().length)return null;let n;try{n=JSON.parse(e[2])}catch{return null}let r=JA(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function Il(t,e,n){let r=KA.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Rl(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=jm}={}){if(!Array.isArray(t)||!e||!_l.has(n))return null;let s=null;for(let l=t.length-1;l>=0;l--){let u=t[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==e||s.agent!==n)return null;let a=s.ask?.questions||El(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Il(n,s.sourceId||s.id,a);return typeof r=="function"&&r(c)?null:{provider:n,box:e,promptId:c,groupId:c,answerMode:"say",source:"transcript",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function Lm(t,e,n){let r=[];(t?.questions||[]).forEach((a,i)=>{let c=(e?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=Xo(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
83
83
  ${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
84
84
  `)}`:s=o,r.length&&o&&(s+=`
85
- ${o}`),s||""}var Om=`
85
+ ${o}`),s||""}var Fm=`
86
86
  When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
87
87
  requirement, an irreversible choice, several valid approaches \u2014 do not bury the
88
88
  question in prose. End your message with exactly ONE fenced code block tagged
@@ -102,79 +102,80 @@ your turn. The operator sees the questions as an interactive card in their chat
102
102
  answer. Never use the block for rhetorical questions, progress updates, or
103
103
  anything you can decide yourself; keep asking in prose for questions without
104
104
  enumerable options.
105
- `;var rt=t=>t.payload?.ts??t.ts;function $A(t){return{id:t.id,kind:"prompt",groupId:t.groupId,provider:t.provider,box:t.box,agent:t.provider,questionTotal:t.questions.length,answeredVia:"chat",questions:t.questions.map((e,n)=>({index:n,header:e.header||"",question:e.question,multiSelect:!!e.multiSelect,chosen:[]})),ts:t.ts}}var OA=new Set(["opened","ci-fixed","reconciled","pushed"]),Mm=t=>`${t.repo??""}#${t.number}`;function MA(t,e,n){let r=Mm(n),o=t.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of t){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?t.get(o):t.get(s);if(o&&o!==s){t.delete(o);let i=e.get(o);if(e.delete(o),i?.length){let c=e.get(s)??[];e.set(s,c.concat(i))}}return t.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function zn(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$/,f=new Map;for(let g of t||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let w=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(w))for(let A of w.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let E=A[0].split("/").pop().toLowerCase();f.has(E)||f.set(E,[]),f.get(E).push(g.payload?.ts??g.ts)}}let d=3*6e4,p=20*6e4,m=(g,w)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some(E=>E>=w-d&&E<=w+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let w=vt(g.payload?.text);w&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(w))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let w=vt(g.payload?.text);w&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(w))}else if(g.type==="job.started"&&g.payload?.id)a.add(g.payload.id),(g.payload.kind||"agent")==="agent"&&g.payload.name&&c.add(g.payload.name);else if(g.type==="turn.user"&&g.payload?.note?.kind==="task"){let w=g.payload.note,A={status:w.status||null,summary:w.summary||null,resultHead:w.resultHead??null,ts:rt(g)};w.toolUseId&&i.set(w.toolUseId,A);let E=String(w.summary||"").match(u);E&&l.set(E[1],A)}let h=new Map,y=new Map,k=null,v=new Map,I=new Map,R=new Map,z=new Map,O=new Map,M=new Map,S=new Map,N=()=>{for(let g of S.values())g.questions.length&&e.push(g);S.clear()},D=null,it=6e4,ct=new Map,T=(g,w)=>{let A=o.get(g);if(!A?.length)return!1;let E=vt(w),J=s.get(g);return A.some(W=>{if(!E.startsWith(W))return!1;let at=vt(E.slice(W.length));return!!at&&!!J?.has(at)})};for(let g of t||[]){let w=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(w.text||"").trim()&&!w.images?.length)break;{let A=w.note||Ma(w.text);if(A){if(A.kind==="task"){if(A.toolUseId&&a.has(A.toolUseId))break;let E=String(A.summary||"").match(u);if(!A.toolUseId&&E&&c.has(E[1]))break}if(A.kind==="command"&&Am(A.command))break;if(A.kind==="resume-summary"){e.push({id:g.id,kind:"resume-summary",title:A.title||"Context resumed from summary",bullets:A.bullets||[],sections:A.sections||[],rawChars:A.rawChars??null,ts:rt(g),box:g.boxId,agent:g.provider});break}A.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:Em(A),name:A.name??null,command:A.command??null,summary:A.summary??null,ts:rt(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${rt(g)}`)||g.source==="transcript"&&T(g.boxId,w.text))break;if(w.kind==="merge-notify"||w.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:w.kind==="ship-notify"?"ship-notified":"merge-notified",text:w.text,ts:rt(g),box:g.boxId,agent:g.provider});break}if(w.kind==="mock-picked"||w.kind==="ci-fix"||w.kind==="reconcile"||w.kind==="pr-go"||w.kind==="push-go"||w.kind==="spin-up-view"||w.kind==="prompt-answer"){w.kind==="prompt-answer"&&(D=null);break}for(let A of v.values())A.status="stale";v.clear(),D&&D.box===g.boxId&&(e.push($A(D)),D=null),e.push({id:g.id,kind:"message",role:"user",text:w.text,ts:rt(g),box:g.boxId,agent:g.provider,boxRole:w.boxRole,...g.actor==="orchestrator"?{orchestration:w.kind||!0}:{},...w.sourceId?{sourceId:w.sourceId}:{},...w.attachments?.length?{attachments:w.attachments}:{},...w.images?.length?{images:w.images}:{}});break}case"peer.message":{let A=w.from||{};e.push({id:g.id,kind:"event",type:"peer-message",text:w.text,from:A.trackName||A.boxId||A.name||null,fromTrackId:A.trackId||null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(w.model==="<synthetic>"&&/^No response requested\.$/.test(String(w.text||"").trim()))break;N();let A=(w.images||[]).filter(at=>!m(at,rt(g)));if(!String(w.text||"").trim()&&!A.length)break;if(g.provider==="cursor"){let at=`${g.boxId}|${w.text}`;if(n.has(at))break;n.add(at)}(!D||D.box===g.boxId)&&(D=null);let E=g.provider==="codex"?(w.tools||[]).filter(at=>!ml.has(at)&&!Ko.has(at)):w.tools||[],J=w.text,W=null;if(xl.has(g.provider)){let at=vl(String(w.text||""));at&&(J=at.text,W={questions:at.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:J,ts:rt(g),box:g.boxId,agent:g.provider,boxRole:w.boxRole,...W?{ask:W}:{},...E.length?{tools:E}:{},...w.model?{model:w.model}:{},...w.sourceId?{sourceId:w.sourceId}:{},...A.length?{images:A}:{}}),W&&(D={id:g.id,groupId:Sl(g.provider,w.sourceId||g.id,W.questions),provider:g.provider,box:g.boxId,questions:W.questions,ts:rt(g)});break}case"job.started":{if(!w.id)break;let A=rt(g),E=i.get(w.id);if(!E&&(w.kind||"agent")==="agent"&&w.name){let Tt=l.get(w.name);Tt&&Tt.ts>=A&&(E=Tt)}let J={jobId:w.id,jobKind:w.kind||"agent",name:w.name||null,detail:w.detail||null,status:E?E.status&&E.status!=="completed"?"failed":"done":"running",startedTs:A,finishedTs:E?.ts??null,summary:E?.summary??null,resultHead:E?.resultHead??null},W=ct.get(g.boxId),at=W?.jobs[W.jobs.length-1]?.startedTs;if(W&&at!=null&&A-at<=it)W.jobs.push(J);else{let Tt={id:g.id,kind:"job-dock",dockId:w.id,ts:A,box:g.boxId,agent:g.provider,jobs:[J]};ct.set(g.boxId,Tt),e.push(Tt)}break}case"handoff.done":e.push(ge(g,"lead-handoff"));break;case"subtask.delegated":e.push({...ge(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(ge(g,"subtask-merged"));break;case"pr.state.changed":{if(!w.change)break;if(w.change==="opened"){for(let E of v.values())E.status="stale";v.clear()}let A=ge(g,`pr-${w.change}`);if(A.box=g.boxId??null,OA.has(w.change)&&w.number!=null){let E=MA(h,y,w);if(h.size>=2){for(let W of y.values())for(let at of W)at.grouped=!0;A.groupPrs=[...h.values()];let J=[A];for(let W of h.keys())y.set(W,J)}else{let J=y.get(E)??[];J.push(A),y.set(E,J)}}else if(w.change==="merged"||w.change==="closed"||w.change==="lost"){let E=Mm(w),J=k&&!k.complete||h.size>=2;if(h.has(E))h.delete(E),y.delete(E);else if(w.number!=null)for(let W of[...h.keys()])W.endsWith(`#${w.number}`)&&(h.delete(W),y.delete(W));if(J&&w.number!=null){(!k||k.complete)&&(k={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:rt(g),ts:rt(g),box:g.boxId,agent:g.provider},e.push(k)),k.prs.push({number:w.number,url:w.url??null,title:w.title??null,repo:w.repo??null,branch:w.branch??null,change:w.change,ts:rt(g)}),k.remaining=[...h.values()],k.complete=h.size===0;break}}e.push(A);break}case"pr.offered":{if(!w.offerId)break;for(let E of v.values())E.status="stale";v.clear();let A={id:g.id,kind:"pr-offer",offerId:w.offerId,excerpt:w.excerpt||null,branch:w.branch||null,base:w.base||null,offered:Array.isArray(w.offered)&&w.offered.length?w.offered:null,toPr:w.toPr&&typeof w.toPr=="object"?{number:w.toPr.number??null}:null,repos:Array.isArray(w.repos)&&w.repos.length?w.repos:null,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};v.set(w.offerId,A),I.set(w.offerId,A),e.push(A);break}case"pr.offer.answered":{let A=I.get(w.offerId);A&&(A.status=w.action||"dismiss",A.actions=Array.isArray(w.actions)?w.actions:null,A.notify=w.notify===!0,v.delete(w.offerId));break}case"box.state.changed":{let A=w.change==="model"?"model-change":w.change;if(!A)break;let E=ge(g,A);A==="model-change"&&(E.to=w.model),e.push(E);break}case"view.state.changed":{if(w.change!=="created"||w.restarted)break;e.push({id:g.id,kind:"view-card",viewId:w.viewId,name:w.name,slug:w.slug,port:w.port,origin:w.origin,ts:rt(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:w.name||null,reason:w.reason||"no-view",ts:rt(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(w.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:w.mockId,name:w.name,group:w.group??null,version:w.version??null,siblings:w.siblings??[],ts:rt(g),box:g.boxId,agent:g.provider});break}if(w.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:w.mockId,name:w.name,group:w.group??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(w.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:w.flowId,name:w.name,stepCount:w.stepCount??null,mockIds:w.mockIds??[],ts:rt(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(w.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:w.runbookId,name:w.name,ts:rt(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:w.provider||"linear",key:w.key,url:w.url,title:w.title??null,linkSource:w.linkSource??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"ticket.move.offered":{if(!w.offerId)break;for(let E of R.values())E.key===w.key&&(E.status="stale",R.delete(E.offerId));let A={id:g.id,kind:"ticket-offer",offerId:w.offerId,provider:w.provider||"linear",key:w.key,url:w.url??null,phase:w.phase??null,from:w.from??null,to:w.to?.name??null,prNumber:w.prNumber??null,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};R.set(w.offerId,A),z.set(w.offerId,A),e.push(A);break}case"ticket.move.answered":{let A=z.get(w.offerId);A&&(A.status=w.action==="moved"?"moved":"dismiss",R.delete(w.offerId));break}case"ticket.state.changed":{for(let A of R.values())A.key===w.key&&A.to===w.to&&(A.status="stale",R.delete(A.offerId));if(w.mode==="offer")break;e.push(ge(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:w.key,url:w.url??null,excerpt:w.excerpt??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(ge(g,"ticket-flow"));break;case"ticket.create.offered":{if(!w.proposalId)break;for(let E of O.values())E.status="stale";O.clear();let A={id:g.id,kind:"ticket-create-offer",proposalId:w.proposalId,provider:w.provider||"linear",epic:w.epic??null,issues:Array.isArray(w.issues)?w.issues:[],team:w.team??null,needsTeam:!!w.needsTeam,linkCurrentTrack:!!w.linkCurrentTrack,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};O.set(w.proposalId,A),M.set(w.proposalId,A),e.push(A);break}case"ticket.create.answered":{let A=M.get(w.proposalId);A&&(A.status=w.action==="created"?"created":"dismiss",A.createdCount=w.createdCount??null,A.failedCount=w.failedCount??null,O.delete(w.proposalId));break}case"ticket.created":{let A={items:Array.isArray(w.items)?w.items:[],failed:Array.isArray(w.failed)?w.failed:[],team:w.team??null},E=w.proposalId?M.get(w.proposalId):null;if(E){E.created=A;break}e.push({id:g.id,kind:"ticket-created",...A,ts:rt(g),box:g.boxId,agent:g.provider});break}case"error":{if(!w.kind)break;e.push(ge(g,w.kind));break}case"usage.cap.hit":e.push(ge(g,"usage-cap-hit"));break;case"usage.cap.warning":e.push(ge(g,"usage-cap-warning"));break;case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:w.provider||g.provider,ts:rt(g),at:rt(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(D=null,w.control||!w.question)break;let A=w.groupId||w.promptId;if(!A)break;let E=S.get(A);E||(E={id:g.id,kind:"prompt",groupId:A,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:w.questionTotal??1,questions:[],ts:rt(g)},S.set(A,E));let J=w.questionIndex??0,W={index:J,header:w.header||"",question:w.question,multiSelect:!!w.multiSelect,chosen:Array.isArray(w.chosen)?w.chosen:[]},at=E.questions.findIndex(Tt=>Tt.index===J&&vt(Tt.question)===vt(W.question));at>=0?E.questions[at]=W:E.questions.push(W),E.questions.sort((Tt,Xs)=>Tt.index-Xs.index),E.questionTotal=Math.max(E.questionTotal,w.questionTotal??1),E.questions.length>=E.questionTotal&&(e.push(E),S.delete(A));break}default:break}}return NA(e.sort((g,w)=>(g.ts??0)-(w.ts??0)))}var CA=2*6e4;function NA(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<=CA){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 Al(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:PA(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??zn(t)).filter(o=>o.kind==="message");return fm(n,r)}function PA(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Cr(t.command)?e:mn(t.command,t.description||"")}function ge(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:rt(t),ts:rt(t)}}var DA=12e4,BA=new Set(["archived","unarchived","opened"]);function Cm(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+DA,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&BA.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var jA=2500,LA=new Set(["pr-opened","pr-merged","pr-auto-armed"]);function Nm(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||!LA.has(r.type)||!r.box)continue;let o=(r.ts??0)+jA,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var FA=12,UA=2e3,Pm=48e3,HA=30,WA=10;function qA(t){let e=null,n=null,r=null;for(let o of t??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(e={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:e,lastPush:n,lastEventAt:r}}var KA=(t,e)=>{let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1)}\u2026`},Dm=(t,e)=>t&&e?Math.max(1,Math.round((t-e)/6e4)):null;function GA(t){for(let e=(t?.length??0)-1;e>=0;e--){let n=t[e];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function VA(t,e){let n=[],r=[],o=new Set,s=new Set;for(let a=(t?.length??0)-1;a>=0;a--){let i=t[a];if(i?.type!=="tool.called")continue;if(e!=null&&(i.ts??0)<=e)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<HA&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<WA&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}var Bm={failover:"**This is a reconstruction, not a checkpoint.** The node this Track lived on went offline without a clean handoff; bostrat assembled this brief from the durable event log. Treat every claim below as a lead, not a fact \u2014 **verify the repository state first** (git status, git log, the diff against the last pushed ref) before continuing work.","in-place":"**This is a reconstruction, not a checkpoint.** The previous lead ended without writing one (out of credits, capped, or unresponsive); bostrat assembled this status from the Track's event log. You are in the SAME live workspace \u2014 uncommitted work is present locally. **Run git status and git log first** and treat every claim below as a lead, not a fact."};function jm(t,{trackName:e="this Track",now:n=null,mode:r="failover"}={}){let o=qA(t),s=GA(t),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=zn(t??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-FA);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${e}`),l.push(""),l.push(Bm[r]??Bm.failover),l.push(""),o.lastPush?.sha){let d=Dm(n,o.lastPush.ts),p=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let d=Dm(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=VA(t??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${KA(d.text,UA)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
106
- `);return f.length<=Pm?f:`${f.slice(0,Pm)}
105
+ `;var ot=t=>t.payload?.ts??t.ts;function YA(t){return{id:t.id,kind:"prompt",groupId:t.groupId,provider:t.provider,box:t.box,agent:t.provider,questionTotal:t.questions.length,answeredVia:"chat",questions:t.questions.map((e,n)=>({index:n,header:e.header||"",question:e.question,multiSelect:!!e.multiSelect,chosen:[]})),ts:t.ts}}var XA=new Set(["opened","ci-fixed","reconciled","pushed"]),Um=t=>`${t.repo??""}#${t.number}`;function ZA(t,e,n){let r=Um(n),o=t.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of t){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?t.get(o):t.get(s);if(o&&o!==s){t.delete(o);let i=e.get(o);if(e.delete(o),i?.length){let c=e.get(s)??[];e.set(s,c.concat(i))}}return t.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function Jn(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$/,f=new Map;for(let b of t||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let w=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(w))for(let v of w.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let E=v[0].split("/").pop().toLowerCase();f.has(E)||f.set(E,[]),f.get(E).push(b.payload?.ts??b.ts)}}let d=3*6e4,p=20*6e4,m=(b,w)=>!!(b?.name&&f.get(String(b.name).toLowerCase()))?.some(E=>E>=w-d&&E<=w+p);for(let b of t||[])if(b.type==="turn.retracted"){r.add(`${b.boxId}|${b.payload?.saidTs}`);let w=Tt(b.payload?.text);w&&(o.has(b.boxId)||o.set(b.boxId,[]),o.get(b.boxId).push(w))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let w=Tt(b.payload?.text);w&&(s.has(b.boxId)||s.set(b.boxId,new Set),s.get(b.boxId).add(w))}else if(b.type==="job.started"&&b.payload?.id)a.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&c.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let w=b.payload.note,v={status:w.status||null,summary:w.summary||null,resultHead:w.resultHead??null,ts:ot(b)};w.toolUseId&&i.set(w.toolUseId,v);let E=String(w.summary||"").match(u);E&&l.set(E[1],v)}let h=new Map,g=new Map,k=null,S=new Map,R=new Map,$=new Map,J=new Map,O=new Map,N=new Map,A=new Map,C=new Map,F=new Map,ct=()=>{for(let b of F.values())b.questions.length&&e.push(b);F.clear()},Y=null,T=6e4,L=new Map,_t=(b,w)=>{let v=o.get(b);if(!v?.length)return!1;let E=Tt(w),pt=s.get(b);return v.some(q=>{if(!E.startsWith(q))return!1;let at=Tt(E.slice(q.length));return!!at&&!!pt?.has(at)})};for(let b of t||[]){let w=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"||!String(w.text||"").trim()&&!w.images?.length)break;{let v=w.note||Ba(w.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let E=String(v.summary||"").match(u);if(!v.toolUseId&&E&&c.has(E[1]))break}if(v.kind==="command"&&Nm(v.command))break;if(v.kind==="resume-summary"){e.push({id:b.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:ot(b),box:b.boxId,agent:b.provider});break}v.kind!=="suppress"&&e.push({id:b.id,kind:"event",type:Dm(v),name:v.name??null,command:v.command??null,summary:v.summary??null,ts:ot(b),box:b.boxId,agent:b.provider});break}}if(r.has(`${b.boxId}|${ot(b)}`)||b.source==="transcript"&&_t(b.boxId,w.text))break;if(w.kind==="merge-notify"||w.kind==="ship-notify"){e.push({id:b.id,kind:"event",type:w.kind==="ship-notify"?"ship-notified":"merge-notified",text:w.text,ts:ot(b),box:b.boxId,agent:b.provider});break}if(w.kind==="mock-picked"||w.kind==="ci-fix"||w.kind==="reconcile"||w.kind==="pr-go"||w.kind==="push-go"||w.kind==="spin-up-view"||w.kind==="prompt-answer"){w.kind==="prompt-answer"&&(Y=null);break}for(let v of S.values())v.status="stale";S.clear(),Y&&Y.box===b.boxId&&(e.push(YA(Y)),Y=null),e.push({id:b.id,kind:"message",role:"user",text:w.text,ts:ot(b),box:b.boxId,agent:b.provider,boxRole:w.boxRole,...b.actor==="orchestrator"?{orchestration:w.kind||!0}:{},...w.sourceId?{sourceId:w.sourceId}:{},...w.attachments?.length?{attachments:w.attachments}:{},...w.images?.length?{images:w.images}:{}});break}case"peer.message":{let v=w.from||{};e.push({id:b.id,kind:"event",type:"peer-message",text:w.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:ot(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(w.model==="<synthetic>"&&/^No response requested\.$/.test(String(w.text||"").trim()))break;ct();let v=(w.images||[]).filter(at=>!m(at,ot(b)));if(!String(w.text||"").trim()&&!v.length)break;if(b.provider==="cursor"){let at=`${b.boxId}|${w.text}`;if(n.has(at))break;n.add(at)}(!Y||Y.box===b.boxId)&&(Y=null);let E=b.provider==="codex"?(w.tools||[]).filter(at=>!bl.has(at)&&!zo.has(at)):w.tools||[],pt=w.text,q=null;if(_l.has(b.provider)){let at=El(String(w.text||""));at&&(pt=at.text,q={questions:at.questions})}e.push({id:b.id,kind:"message",role:"assistant",text:pt,ts:ot(b),box:b.boxId,agent:b.provider,boxRole:w.boxRole,...q?{ask:q}:{},...E.length?{tools:E}:{},...w.model?{model:w.model}:{},...w.sourceId?{sourceId:w.sourceId}:{},...v.length?{images:v}:{}}),q&&(Y={id:b.id,groupId:Il(b.provider,w.sourceId||b.id,q.questions),provider:b.provider,box:b.boxId,questions:q.questions,ts:ot(b)});break}case"job.started":{if(!w.id)break;let v=ot(b),E=i.get(w.id);if(!E&&(w.kind||"agent")==="agent"&&w.name){let Et=l.get(w.name);Et&&Et.ts>=v&&(E=Et)}let pt={jobId:w.id,jobKind:w.kind||"agent",name:w.name||null,detail:w.detail||null,status:E?E.status&&E.status!=="completed"?"failed":"done":"running",startedTs:v,finishedTs:E?.ts??null,summary:E?.summary??null,resultHead:E?.resultHead??null},q=L.get(b.boxId),at=q?.jobs[q.jobs.length-1]?.startedTs;if(q&&at!=null&&v-at<=T)q.jobs.push(pt);else{let Et={id:b.id,kind:"job-dock",dockId:w.id,ts:v,box:b.boxId,agent:b.provider,jobs:[pt]};L.set(b.boxId,Et),e.push(Et)}break}case"handoff.done":e.push(be(b,"lead-handoff"));break;case"subtask.delegated":e.push({...be(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":e.push(be(b,"subtask-merged"));break;case"pr.state.changed":{if(!w.change)break;if(w.change==="opened"){for(let E of S.values())E.status="stale";S.clear()}let v=be(b,`pr-${w.change}`);if(v.box=b.boxId??null,XA.has(w.change)&&w.number!=null){let E=ZA(h,g,w);if(h.size>=2){for(let q of g.values())for(let at of q)at.grouped=!0;v.groupPrs=[...h.values()];let pt=[v];for(let q of h.keys())g.set(q,pt)}else{let pt=g.get(E)??[];pt.push(v),g.set(E,pt)}}else if(w.change==="merged"||w.change==="closed"||w.change==="lost"){let E=Um(w),pt=k&&!k.complete||h.size>=2;if(h.has(E))h.delete(E),g.delete(E);else if(w.number!=null)for(let q of[...h.keys()])q.endsWith(`#${w.number}`)&&(h.delete(q),g.delete(q));if(pt&&w.number!=null){(!k||k.complete)&&(k={id:b.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:ot(b),ts:ot(b),box:b.boxId,agent:b.provider},e.push(k)),k.prs.push({number:w.number,url:w.url??null,title:w.title??null,repo:w.repo??null,branch:w.branch??null,change:w.change,ts:ot(b)}),k.remaining=[...h.values()],k.complete=h.size===0;break}}e.push(v);break}case"pr.offered":{if(!w.offerId)break;for(let E of S.values())E.status="stale";S.clear();let v={id:b.id,kind:"pr-offer",offerId:w.offerId,excerpt:w.excerpt||null,branch:w.branch||null,base:w.base||null,offered:Array.isArray(w.offered)&&w.offered.length?w.offered:null,toPr:w.toPr&&typeof w.toPr=="object"?{number:w.toPr.number??null}:null,repos:Array.isArray(w.repos)&&w.repos.length?w.repos:null,status:"open",ts:ot(b),box:b.boxId,agent:b.provider};S.set(w.offerId,v),R.set(w.offerId,v),e.push(v);break}case"pr.offer.answered":{let v=R.get(w.offerId);v&&(v.status=w.action||"dismiss",v.actions=Array.isArray(w.actions)?w.actions:null,v.notify=w.notify===!0,S.delete(w.offerId));break}case"box.state.changed":{let v=w.change==="model"?"model-change":w.change;if(!v)break;let E=be(b,v);v==="model-change"&&(E.to=w.model),e.push(E);break}case"view.state.changed":{if(w.change!=="created"||w.restarted)break;e.push({id:b.id,kind:"view-card",viewId:w.viewId,name:w.name,slug:w.slug,port:w.port,origin:w.origin,ts:ot(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{e.push({id:b.id,kind:"view-spinup-failed",name:w.name||null,reason:w.reason||"no-view",ts:ot(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(w.change==="picked"){e.push({id:b.id,kind:"mock-picked",mockId:w.mockId,name:w.name,group:w.group??null,version:w.version??null,siblings:w.siblings??[],ts:ot(b),box:b.boxId,agent:b.provider});break}if(w.change!=="created")break;e.push({id:b.id,kind:"mock-card",mockId:w.mockId,name:w.name,group:w.group??null,ts:ot(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(w.change!=="created")break;e.push({id:b.id,kind:"flow-card",flowId:w.flowId,name:w.name,stepCount:w.stepCount??null,mockIds:w.mockIds??[],ts:ot(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(w.change!=="created")break;e.push({id:b.id,kind:"runbook-card",runbookId:w.runbookId,name:w.name,ts:ot(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{e.push({id:b.id,kind:"ticket-card",provider:w.provider||"linear",key:w.key,url:w.url,title:w.title??null,linkSource:w.linkSource??null,ts:ot(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!w.offerId)break;for(let E of O.values())E.box===(w.box||b.boxId)&&(E.status="stale",O.delete(E.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:w.offerId,status:"open",ts:ot(b),box:w.box||b.boxId,agent:w.agent||b.provider};O.set(w.offerId,v),N.set(w.offerId,v),e.push(v);break}case"box.fullscreen.answered":{let v=N.get(w.offerId);v&&(v.status=w.action==="restarted"?"restarted":w.action==="cleared"?"cleared":"dismiss",O.delete(w.offerId));break}case"ticket.move.offered":{if(!w.offerId)break;for(let E of $.values())E.key===w.key&&(E.status="stale",$.delete(E.offerId));let v={id:b.id,kind:"ticket-offer",offerId:w.offerId,provider:w.provider||"linear",key:w.key,url:w.url??null,phase:w.phase??null,from:w.from??null,to:w.to?.name??null,prNumber:w.prNumber??null,status:"open",ts:ot(b),box:b.boxId,agent:b.provider};$.set(w.offerId,v),J.set(w.offerId,v),e.push(v);break}case"ticket.move.answered":{let v=J.get(w.offerId);v&&(v.status=w.action==="moved"?"moved":"dismiss",$.delete(w.offerId));break}case"ticket.state.changed":{for(let v of $.values())v.key===w.key&&v.to===w.to&&(v.status="stale",$.delete(v.offerId));if(w.mode==="offer")break;e.push(be(b,"ticket-state"));break}case"ticket.commented":{e.push({id:b.id,kind:"ticket-comment",key:w.key,url:w.url??null,excerpt:w.excerpt??null,ts:ot(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":e.push(be(b,"ticket-flow"));break;case"ticket.create.offered":{if(!w.proposalId)break;for(let E of A.values())E.status="stale";A.clear();let v={id:b.id,kind:"ticket-create-offer",proposalId:w.proposalId,provider:w.provider||"linear",epic:w.epic??null,issues:Array.isArray(w.issues)?w.issues:[],team:w.team??null,needsTeam:!!w.needsTeam,linkCurrentTrack:!!w.linkCurrentTrack,status:"open",ts:ot(b),box:b.boxId,agent:b.provider};A.set(w.proposalId,v),C.set(w.proposalId,v),e.push(v);break}case"ticket.create.answered":{let v=C.get(w.proposalId);v&&(v.status=w.action==="created"?"created":"dismiss",v.createdCount=w.createdCount??null,v.failedCount=w.failedCount??null,A.delete(w.proposalId));break}case"ticket.created":{let v={items:Array.isArray(w.items)?w.items:[],failed:Array.isArray(w.failed)?w.failed:[],team:w.team??null},E=w.proposalId?C.get(w.proposalId):null;if(E){E.created=v;break}e.push({id:b.id,kind:"ticket-created",...v,ts:ot(b),box:b.boxId,agent:b.provider});break}case"error":{if(!w.kind)break;e.push(be(b,w.kind));break}case"usage.cap.hit":e.push(be(b,"usage-cap-hit"));break;case"usage.cap.warning":e.push(be(b,"usage-cap-warning"));break;case"provider.connected":{e.push({id:b.id,kind:"event",type:"provider-connected",provider:w.provider||b.provider,ts:ot(b),at:ot(b),box:b.boxId,agent:b.provider});break}case"prompt.answered":{if(Y=null,w.control||!w.question)break;let v=w.groupId||w.promptId;if(!v)break;let E=F.get(v);E||(E={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:w.questionTotal??1,questions:[],ts:ot(b)},F.set(v,E));let pt=w.questionIndex??0,q={index:pt,header:w.header||"",question:w.question,multiSelect:!!w.multiSelect,chosen:Array.isArray(w.chosen)?w.chosen:[]},at=E.questions.findIndex(Et=>Et.index===pt&&Tt(Et.question)===Tt(q.question));at>=0?E.questions[at]=q:E.questions.push(q),E.questions.sort((Et,yr)=>Et.index-yr.index),E.questionTotal=Math.max(E.questionTotal,w.questionTotal??1),E.questions.length>=E.questionTotal&&(e.push(E),F.delete(v));break}default:break}}return t_(e.sort((b,w)=>(b.ts??0)-(w.ts??0)))}var QA=2*6e4;function t_(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<=QA){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 $l(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:e_(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??Jn(t)).filter(o=>o.kind==="message");return xm(n,r)}function e_(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Pr(t.command)?e:hn(t.command,t.description||"")}function be(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:ot(t),ts:ot(t)}}var n_=12e4,r_=new Set(["archived","unarchived","opened"]);function Hm(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+n_,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&r_.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var o_=2500,s_=new Set(["pr-opened","pr-merged","pr-auto-armed"]);function Wm(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||!s_.has(r.type)||!r.box)continue;let o=(r.ts??0)+o_,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var a_=12,i_=2e3,qm=48e3,c_=30,l_=10;function u_(t){let e=null,n=null,r=null;for(let o of t??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(e={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:e,lastPush:n,lastEventAt:r}}var d_=(t,e)=>{let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1)}\u2026`},Km=(t,e)=>t&&e?Math.max(1,Math.round((t-e)/6e4)):null;function f_(t){for(let e=(t?.length??0)-1;e>=0;e--){let n=t[e];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function p_(t,e){let n=[],r=[],o=new Set,s=new Set;for(let a=(t?.length??0)-1;a>=0;a--){let i=t[a];if(i?.type!=="tool.called")continue;if(e!=null&&(i.ts??0)<=e)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<c_&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<l_&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}var Gm={failover:"**This is a reconstruction, not a checkpoint.** The node this Track lived on went offline without a clean handoff; bostrat assembled this brief from the durable event log. Treat every claim below as a lead, not a fact \u2014 **verify the repository state first** (git status, git log, the diff against the last pushed ref) before continuing work.","in-place":"**This is a reconstruction, not a checkpoint.** The previous lead ended without writing one (out of credits, capped, or unresponsive); bostrat assembled this status from the Track's event log. You are in the SAME live workspace \u2014 uncommitted work is present locally. **Run git status and git log first** and treat every claim below as a lead, not a fact."};function Vm(t,{trackName:e="this Track",now:n=null,mode:r="failover"}={}){let o=u_(t),s=f_(t),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Jn(t??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-a_);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${e}`),l.push(""),l.push(Gm[r]??Gm.failover),l.push(""),o.lastPush?.sha){let d=Km(n,o.lastPush.ts),p=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let d=Km(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=p_(t??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${d_(d.text,i_)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
106
+ `);return f.length<=qm?f:`${f.slice(0,qm)}
107
107
 
108
- [\u2026 brief truncated]`}var Wm=".bostrat/handoff",qm="latest.md",Lm=32*1024;function Km(t){let e=Hn(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=Lm?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,Lm).toString("utf8").replace(/�+$/,"")}
108
+ [\u2026 brief truncated]`}var Zm=".bostrat/handoff",Qm="latest.md",zm=32*1024;function th(t){let e=Wn(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=zm?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,zm).toString("utf8").replace(/�+$/,"")}
109
109
 
110
- [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var Gm="Checkpoint: write a work-in-progress handoff",Vm="HANDOFF WRITTEN",zm="Resume from .bostrat/handoff/latest.md",JA="/clear",XA=t=>`${Gm} 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 ${Vm} and take no further action.`,YA=`${zm}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Jm="Beat: write a",Xm="BEAT WRITTEN",ZA=".bostrat/arc",QA="beat.txt",Fm=200,Ym=4,Zm=5,t_=36,e_=t=>`${Jm} short summary of what this workstream accomplished, 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). Format the file as: FIRST LINE = one sentence of at most 18 words, plain past tense, saying the outcome \u2014 no heading, no bullet, no trailing list. THEN, only if this workstream covered several separable pieces of work, up to ${Ym} further lines, each starting with "- " and naming ONE piece in at most ${Zm} words \u2014 a label, not a sentence (e.g. "- stale working flag on poll"). Write nothing else in the file. When the file is written, reply only with ${Xm} and take no further action.`,Um=/^(?:[-–—*•·]|\d+[.)])\s+/,n_=(t,e,n)=>{let r=t.split(/\s+/).filter(Boolean).slice(0,e).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};function r_(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Um.test(s)){if(r.length>=Ym)continue;let a=n_(s.replace(Um,"").replace(/[.;,:]+$/,""),Zm,t_);a&&r.push(a)}else r.length||n.push(s);let o=n.join(" ").replace(/\s+/g," ").replace(/[:\-–—]+$/,"").trim();if(!o)throw new Error("beat file has no summary line");return o.length>Fm&&(o=`${o.slice(0,Fm-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Qm(t,e,{timeoutMs:n=9e4}={}){let r=wn.join(e,ZA),o=wn.join(r,QA);await hn.mkdir(r,{recursive:!0});let s=await Ca(o),a=await tt(t);if(!a)throw new Error(`session "${t}" not found`);return await Na(t,a,e_(o)),await ch(o,s,n),r_(await hn.readFile(o,"utf8"))}function th(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Vm||n===Xm:n.startsWith(Gm)||n===JA||n.startsWith(zm)||n.startsWith(Jm):!1}var o_=6e5;function eh(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-o_;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 nh(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 rh(t,{windows:e=[]}={}){if(th(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 s_=3500,a_=3e5,_l=new Map;function Pa(t){return _l.get(t)||null}async function oh(t){let e=_l.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await tt(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:wn.join(n.cwd,Wm,qm)};return _l.set(t,r),i_(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function i_(t,e){t.step="summarize",await sh(t.name,e),t.step="clear";let n=await tt(t.name);await Na(t.name,n,"/clear"),await lh(s_),t.step="resume",await Na(t.name,n,YA),t.step="done",t.finishedAt=Date.now()}async function sh(t,e,{timeoutMs:n=a_}={}){let r=wn.join(e,Wm),o=wn.join(r,qm);await hn.mkdir(r,{recursive:!0});let s=await Ca(o),a=await tt(t);await q({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:o,beforeMtime:s}),await Na(t,a,XA(o)),await ch(o,s,n),await q({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:o,afterMtime:await Ca(o)});let i=await om(e,o,{exclude:u=>th(u.role,u.text)});await q({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(c){await $({id:c,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await hn.readFile(o,"utf8");await $({id:c,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Km(u),ts:Date.now()}})}catch{}}await np(e);let l=await c_(r,o);return c&&await pp(c,l||o,{box:t,agent:a?.agent??null}),o}async function ah(t,{timeoutMs:e}={}){let n=await tt(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);return sh(t,n.cwd,e?{timeoutMs:e}:void 0)}var Hm=".bostrat/handoff/status-latest.md";async function ih(t,e,{trackName:n="this Track"}={}){let r=await B({id:t,cwd:e}),o=jm(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=wn.join(e,Hm);return await hn.mkdir(wn.dirname(s),{recursive:!0}),await hn.writeFile(s,o,"utf8"),await $({id:t,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Km(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Hm}async function Ca(t){try{return(await hn.stat(t)).mtimeMs}catch{return 0}}async function ch(t,e,n){let r=Date.now()+n;for(;;){if(await Ca(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await lh(2e3)}}async function c_(t,e){try{if(!zA.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=wn.join(t,`${n}.md`);return await hn.copyFile(e,r),r}catch{return null}}function lh(t){return new Promise(e=>setTimeout(e,t))}async function Na(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 pn({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await tt(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await Et(t,n,{kind:"orchestration-drive",...Pe(r.agent)})}import F from"node:fs/promises";import U from"node:path";import{randomUUID as Ms}from"node:crypto";import{execFile as eO}from"node:child_process";import{promisify as nO}from"node:util";import Zw from"node:path";var El={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},uh={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},l_=/^\d{4}-\d{2}-\d{2}$/;function dh(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!l_.test(String(t)))throw new Error(`pricing.js: ${n} window ${e} must be YYYY-MM-DD, got ${JSON.stringify(t)}`);let r=Date.parse(`${t}T${e==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${e} is not a real date: ${t}`);return r}function u_(t){let e={};for(let[n,r]of Object.entries(t)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);e[n]=r.map(o=>({...o,fromMs:dh(o.from,"from",n),toMs:dh(o.to,"to",n)}))}return e}var d_=u_(El);function f_(t){if(t==null)return Date.now();if(typeof t=="number")return Number.isFinite(t)?t:null;if(t instanceof Date){let n=t.getTime();return Number.isFinite(n)?n:null}let e=Date.parse(String(t));return Number.isFinite(e)?e:null}function p_(t){if(!t)return null;let e=String(t).trim();return El[e]?e:uh[e]?uh[e]:Object.keys(El).find(r=>e.startsWith(r))||null}function fh(t,e){let n=p_(t);if(!n)return null;let r=f_(e);return r===null?null:d_[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}var ph=(t,e)=>fh(t,e)!==null;function gn(t,e={},n){let r=fh(t,n);return r?((e.input||0)*r.input+(e.output||0)*r.output+(e.cacheWrite||0)*r.cacheWrite+(e.cacheRead||0)*r.cacheRead)/1e6:0}var mh=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function m_(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function hh(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function De(t){let e=t.payload?.model||"unknown",n=m_(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=t.source==="vendor",s=r,a=!1;if(n&&!o){let l=gn(e,n,t.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!ph(e,t.ts);return{usd:s,model:e,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function ye(t,{boxLabels:e={}}={}){let n=(t||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=mh();for(let d of n){let{usd:p,model:m,tokens:h,repriced:y,unpriced:k,asRecorded:v}=De(d);i+=v;let I=d.provider||"unknown",R=d.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(m)),a+=p,hh(f,h),r[m]||={usd:0,tokens:mh(),events:0},r[m].usd+=p,r[m].events+=1,hh(r[m].tokens,h),o[I]||={usd:0,events:0},o[I].usd+=p,o[I].events+=1,s[R]||={usd:0,events:0,provider:I,models:new Set,label:e[R]||R},s[R].usd+=p,s[R].events+=1,m!=="unknown"&&s[R].models.add(m),d.epoch!=null){let z=(s[R].epochs||={})[d.epoch]||={usd:0,events:0,provider:I,models:new Set};z.usd+=p,z.events+=1,z.provider=I,m!=="unknown"&&z.models.add(m)}}return{totalUsd:Pr(a),asRecordedUsd:Pr(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Da(r,d=>({...d,usd:Pr(d.usd)})),byProvider:Da(o,d=>({...d,usd:Pr(d.usd)})),byBox:Da(s,d=>({usd:Pr(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Da(d.epochs,p=>({usd:Pr(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Pr=t=>Math.round(t*1e4)/1e4,Da=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import kt from"node:fs/promises";import St from"node:path";import yh from"node:os";import{randomUUID as y_}from"node:crypto";import{execFile as b_}from"node:child_process";import{promisify as k_}from"node:util";import{chromium as x_}from"playwright-core";import Il from"node:os";import{execFile as h_}from"node:child_process";import{promisify as w_}from"node:util";var g_=w_(h_);async function Ba(){if(process.platform!=="darwin")return Il.freemem();try{let{stdout:t}=await g_("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:Il.freemem()}catch{return Il.freemem()}}var Ol=k_(b_),bh=process.env.BOSTRAT_STATE_DIR||St.join(process.env.HOME||yh.tmpdir(),".bostrat"),kh=St.join(bh,"mocks.json"),v_=process.env.BOSTRAT_MOCK_ENGINE||"chromium",S_=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",T_=process.env.BOSTRAT_FIREFOX_BIN||"firefox",A_=1300,Dr=2,__=32,xh=1500,Br=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,E_=700*1024*1024,wh=100*1024*1024,I_=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",R_=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",vh=2e4,se=class extends Error{constructor(e,n=""){super(n?`${e}
110
+ [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var eh="Checkpoint: write a work-in-progress handoff",nh="HANDOFF WRITTEN",rh="Resume from .bostrat/handoff/latest.md",h_="/clear",w_=t=>`${eh} 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 ${nh} and take no further action.`,g_=`${rh}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,oh="Beat: write a",sh="BEAT WRITTEN",y_=".bostrat/arc",b_="beat.txt",Jm=200,ah=4,ih=5,k_=36,x_=t=>`${oh} short summary of what this workstream accomplished, 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). Format the file as: FIRST LINE = one sentence of at most 18 words, plain past tense, saying the outcome \u2014 no heading, no bullet, no trailing list. THEN, only if this workstream covered several separable pieces of work, up to ${ah} further lines, each starting with "- " and naming ONE piece in at most ${ih} words \u2014 a label, not a sentence (e.g. "- stale working flag on poll"). Write nothing else in the file. When the file is written, reply only with ${sh} and take no further action.`,Ym=/^(?:[-–—*•·]|\d+[.)])\s+/,S_=(t,e,n)=>{let r=t.split(/\s+/).filter(Boolean).slice(0,e).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};function v_(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Ym.test(s)){if(r.length>=ah)continue;let a=S_(s.replace(Ym,"").replace(/[.;,:]+$/,""),ih,k_);a&&r.push(a)}else r.length||n.push(s);let o=n.join(" ").replace(/\s+/g," ").replace(/[:\-–—]+$/,"").trim();if(!o)throw new Error("beat file has no summary line");return o.length>Jm&&(o=`${o.slice(0,Jm-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function ch(t,e,{timeoutMs:n=9e4}={}){let r=gn.join(e,y_),o=gn.join(r,b_);await wn.mkdir(r,{recursive:!0});let s=await ja(o),a=await et(t);if(!a)throw new Error(`session "${t}" not found`);return await La(t,a,x_(o)),await gh(o,s,n),v_(await wn.readFile(o,"utf8"))}function lh(t,e){let n=(e||"").trim();if(!n)return!1;if(t==="assistant")return n===nh||n===sh;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(eh)||n===h_||r.startsWith(rh)||r.startsWith(oh)}var T_=6e5;function uh(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-T_;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 dh(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 fh(t,{windows:e=[]}={}){if(lh(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 A_=3500,__=3e5,Ol=new Map;function Fa(t){return Ol.get(t)||null}async function ph(t){let e=Ol.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await et(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:gn.join(n.cwd,Zm,Qm)};return Ol.set(t,r),E_(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function E_(t,e){t.step="summarize",await mh(t.name,e),t.step="clear";let n=await et(t.name);await La(t.name,n,"/clear"),await Cl(A_),t.step="resume",await La(t.name,n,g_),t.step="done",t.finishedAt=Date.now()}async function mh(t,e,{timeoutMs:n=__}={}){let r=gn.join(e,Zm),o=gn.join(r,Qm);await wn.mkdir(r,{recursive:!0});let s=await ja(o),a=await et(t);await K({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:o,beforeMtime:s}),await La(t,a,w_(o)),await gh(o,s,n),await K({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:o,afterMtime:await ja(o)});let i=await pm(e,o,{exclude:u=>lh(u.role,u.text)});await K({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(c){await I({id:c,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await wn.readFile(o,"utf8");await I({id:c,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...th(u),ts:Date.now()}})}catch{}}await dp(e);let l=await I_(r,o);return c&&await Sp(c,l||o,{box:t,agent:a?.agent??null}),o}async function hh(t,{timeoutMs:e}={}){let n=await et(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);return mh(t,n.cwd,e?{timeoutMs:e}:void 0)}var Xm=".bostrat/handoff/status-latest.md";async function wh(t,e,{trackName:n="this Track"}={}){let r=await P({id:t,cwd:e}),o=Vm(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=gn.join(e,Xm);return await wn.mkdir(gn.dirname(s),{recursive:!0}),await wn.writeFile(s,o,"utf8"),await I({id:t,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...th(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Xm}async function ja(t){try{return(await wn.stat(t)).mtimeMs}catch{return 0}}async function gh(t,e,n){let r=Date.now()+n;for(;;){if(await ja(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Cl(2e3)}}async function I_(t,e){try{if(!m_.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=gn.join(t,`${n}.md`);return await wn.copyFile(e,r),r}catch{return null}}function Cl(t){return new Promise(e=>setTimeout(e,t))}async function La(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 mn({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await et(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await kt(t,["Escape"],{kind:"drive-wipe"}),await Cl(150),await Rr(t,va,{kind:"drive-wipe"}),await $t(t,n,{kind:"orchestration-drive",...Be(r.agent)})}import U from"node:fs/promises";import H from"node:path";import{randomUUID as Bs}from"node:crypto";import{execFile as AO}from"node:child_process";import{promisify as _O}from"node:util";import cg from"node:path";var Ml={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},yh={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},R_=/^\d{4}-\d{2}-\d{2}$/;function bh(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!R_.test(String(t)))throw new Error(`pricing.js: ${n} window ${e} must be YYYY-MM-DD, got ${JSON.stringify(t)}`);let r=Date.parse(`${t}T${e==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${e} is not a real date: ${t}`);return r}function $_(t){let e={};for(let[n,r]of Object.entries(t)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);e[n]=r.map(o=>({...o,fromMs:bh(o.from,"from",n),toMs:bh(o.to,"to",n)}))}return e}var O_=$_(Ml);function C_(t){if(t==null)return Date.now();if(typeof t=="number")return Number.isFinite(t)?t:null;if(t instanceof Date){let n=t.getTime();return Number.isFinite(n)?n:null}let e=Date.parse(String(t));return Number.isFinite(e)?e:null}function M_(t){if(!t)return null;let e=String(t).trim();return Ml[e]?e:yh[e]?yh[e]:Object.keys(Ml).find(r=>e.startsWith(r))||null}function kh(t,e){let n=M_(t);if(!n)return null;let r=C_(e);return r===null?null:O_[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}var xh=(t,e)=>kh(t,e)!==null;function yn(t,e={},n){let r=kh(t,n);return r?((e.input||0)*r.input+(e.output||0)*r.output+(e.cacheWrite||0)*r.cacheWrite+(e.cacheRead||0)*r.cacheRead)/1e6:0}var Sh=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function N_(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function vh(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function je(t){let e=t.payload?.model||"unknown",n=N_(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=t.source==="vendor",s=r,a=!1;if(n&&!o){let l=yn(e,n,t.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!xh(e,t.ts);return{usd:s,model:e,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function ke(t,{boxLabels:e={}}={}){let n=(t||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=Sh();for(let d of n){let{usd:p,model:m,tokens:h,repriced:g,unpriced:k,asRecorded:S}=je(d);i+=S;let R=d.provider||"unknown",$=d.boxId||"unattributed";if(g&&c++,k&&(l++,u.add(m)),a+=p,vh(f,h),r[m]||={usd:0,tokens:Sh(),events:0},r[m].usd+=p,r[m].events+=1,vh(r[m].tokens,h),o[R]||={usd:0,events:0},o[R].usd+=p,o[R].events+=1,s[$]||={usd:0,events:0,provider:R,models:new Set,label:e[$]||$},s[$].usd+=p,s[$].events+=1,m!=="unknown"&&s[$].models.add(m),d.epoch!=null){let J=(s[$].epochs||={})[d.epoch]||={usd:0,events:0,provider:R,models:new Set};J.usd+=p,J.events+=1,J.provider=R,m!=="unknown"&&J.models.add(m)}}return{totalUsd:Br(a),asRecordedUsd:Br(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Ua(r,d=>({...d,usd:Br(d.usd)})),byProvider:Ua(o,d=>({...d,usd:Br(d.usd)})),byBox:Ua(s,d=>({usd:Br(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Ua(d.epochs,p=>({usd:Br(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Br=t=>Math.round(t*1e4)/1e4,Ua=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import St from"node:fs/promises";import At from"node:path";import _h from"node:os";import{randomUUID as j_}from"node:crypto";import{execFile as L_}from"node:child_process";import{promisify as F_}from"node:util";import{chromium as U_}from"playwright-core";import Nl from"node:os";import{execFile as P_}from"node:child_process";import{promisify as D_}from"node:util";var B_=D_(P_);async function Ha(){if(process.platform!=="darwin")return Nl.freemem();try{let{stdout:t}=await B_("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:Nl.freemem()}catch{return Nl.freemem()}}var Bl=F_(L_),Eh=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||_h.tmpdir(),".bostrat"),Ih=At.join(Eh,"mocks.json"),H_=process.env.BOSTRAT_MOCK_ENGINE||"chromium",W_=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",q_=process.env.BOSTRAT_FIREFOX_BIN||"firefox",K_=1300,jr=2,G_=32,Rh=1500,Lr=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,V_=700*1024*1024,Th=100*1024*1024,z_=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",J_=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",$h=2e4,ie=class extends Error{constructor(e,n=""){super(n?`${e}
111
111
  ${n.trim().split(`
112
112
  `).slice(-6).join(`
113
- `)}`:e),this.name="MockRenderError"}},Jn=null,Rl=null;async function xe(){return Jn||(Rl||=(async()=>{try{Jn=JSON.parse(await kt.readFile(kh,"utf8"))}catch{Jn={mocks:[]}}finally{Rl=null}return Jn.mocks||=[],Jn})(),Rl)}async function te(){await Ft(kh,Jn)}var be=t=>Pt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),ke=(t,e,n)=>{let r=(t.versions||[]).find(s=>s.v===e),o=n==="mp4"||n==="webm"?Jo(t,r,"video"):r&&Jo(t,r,n)||(n==="html"?ee(t,e).html:null);return o?cn("mocks",t.id,e,n,o).catch(()=>{}):Promise.resolve()};function Sh(t,e){ke(t,e.v,e.videoRel?.endsWith(".webm")?"webm":"mp4"),e.posterRel&&ke(t,e.v,"png")}function Dt(t,e,n={}){return $({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}:{},...t.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}var Xo=t=>St.join(t,".bostrat","mocks");function ee(t,e){let n=Xo(t.cwd);return{html:St.join(n,`${t.id}.v${e}.html`),png:St.join(n,`${t.id}.v${e}.png`)}}function Jo(t,e,n){if(!e)return null;let r=n==="html"?e.htmlRel:n==="png"?e.pngRel??e.posterRel??null:n==="video"?e.videoRel??null:null;return r?St.join(t.cwd,r):null}function ae(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 Ml(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var $_=Buffer.from([137,80,78,71,13,10,26,10]);function ja(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals($_)}function O_(t){return!Buffer.isBuffer(t)||t.length<32?null:t.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:t.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function M_(t){let e=Buffer.alloc(24),n=await kt.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function C_({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 N_(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,h)=>{r.onload=m,r.onerror=()=>h(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,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let h=m*o*4,y=0;y<o;y++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(y<u&&(u=y),y>d&&(d=y),m<f&&(f=m),p=m);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},e.toString("base64"))}async function P_(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((f,d)=>{c.onload=f,c.onerror=()=>d(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 D_({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await x_.launch({executablePath:S_,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new se("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Dr})).newPage();a.setDefaultTimeout(Br),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Br}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Br});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await N_(a,i),u=l&&C_({...l,pad:__*Dr,align:Dr});u&&(i=await P_(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<xh)throw new se(`capture produced a ${i.length}-byte image`);let c=Ml(i);if(!c)throw new se("output is not a PNG");return await kt.writeFile(e,i),{width:Math.round(c.width/Dr),height:Math.round(c.height/Dr),dpr:Dr,renderMs:Date.now()-r}}catch(s){throw s instanceof se?s:new se(/timeout/i.test(s?.message||"")?`render timed out after ${Br}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function gh({htmlPath:t,outPath:e,width:n}){let r=await kt.mkdtemp(St.join(yh.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Ol(T_,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Br,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new se(c?.killed?`render timed out after ${Br}ms`:"firefox exited non-zero",s)}let a=await kt.stat(e).catch(()=>null);if(!a||a.size<xh)throw new se(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Ml(await M_(e));if(!i)throw new se("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await kt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function B_(t){if(v_==="firefox")return gh(t);try{return await D_(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return gh(t);throw e}}var j_=B_;var $l=Promise.resolve();function Th(t){return $l=$l.then(t,t),$l}async function Ah(t,e){let n=await xe(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await te(),await Dt(r,"rendering",{version:e});try{let o=await Ba();if(o<E_)throw new se(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=ee(r,e),a=await j_({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await kt.rm(s.png,{force:!0}).catch(()=>{});return}Cl(r,{v:e,source:"html",htmlRel:St.relative(r.cwd,s.html),pngRel:St.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await te(),await Dt(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),be(r),ke(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await te(),await Dt(r,"error",{version:e,error:r.error}),be(r)}}}function Cl(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 L_({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?A_:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Xn({trackId:t,group:e}={}){let n=await xe(),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 Be(t){let n=(await xe()).mocks.find(r=>r.id===t);return n?{...n}:null}function _h({html:t,image:e,video:n}){if([typeof t=="string"&&t.trim(),!!e,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(e&&!ja(e))throw new Error("image must be a PNG");if(n){let o=O_(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>wh)throw new Error(`video must be ${wh/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function Eh({trackId:t,name:e,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=L_({trackId:t,name:e,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=_h({html:n,image:r,video:o}),m=await xe(),h={id:y_().slice(0,8),trackId:t,box:c||null,agent:l||null,name:e.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:f,height:null,dpr:null,state:"queued",source:d?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await kt.mkdir(Xo(s),{recursive:!0}),m.mocks.push(h),d?(await kt.writeFile(ee(h,1).html,n),await te(),await Dt(h,"created"),be(h),ke(h,1,"html"),Th(()=>Ah(h.id,1))):o?(await Rh(h,1,o,p),await te(),await Dt(h,"created"),await Dt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),be(h),Sh(h,h.versions[0])):(await $h(h,1,r),await te(),await Dt(h,"created"),await Dt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),be(h),ke(h,1,"png")),{...h}}async function Ih(t,{html:e,image:n,video:r,width:o}={}){let a=(await xe()).mocks.find(u=>u.id===t);if(!a)throw new Error("mock not found");let i=typeof e=="string"&&e.trim(),c=_h({html:e,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await kt.mkdir(Xo(a.cwd),{recursive:!0}),i?(a.state="queued",await kt.writeFile(ee(a,l).html,e),await te(),await Dt(a,"updated",{version:l}),be(a),ke(a,l,"html"),Th(()=>Ah(a.id,l))):r?(await Rh(a,l,r,c),await te(),await Dt(a,"updated",{version:l}),await Dt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:ae(a)?.durationS??null}),be(a),Sh(a,a.versions.find(u=>u.v===l))):(await $h(a,l,n),await te(),await Dt(a,"updated",{version:l}),await Dt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),be(a),ke(a,l,"png")),{...a}}async function F_(t){try{let{stdout:e}=await Ol(I_,["-v","error","-print_format","json","-show_format","-show_streams",t],{timeout:vh,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(e),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function U_(t,e){try{await Ol(R_,["-y","-v","error","-i",t,"-frames:v","1","-f","image2",e],{timeout:vh,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await kt.stat(e).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Rh(t,e,n,r){let o=Xo(t.cwd),s=St.join(o,`${t.id}.v${e}.${r.ext}`),a=St.join(o,`${t.id}.v${e}.poster.png`);await kt.writeFile(s,n);let i=await F_(s),c=await U_(s,a);Cl(t,{v:e,source:"video",htmlRel:null,pngRel:null,videoRel:St.relative(t.cwd,s),posterRel:c?St.relative(t.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(t.width=i.width)}async function $h(t,e,n){let r=Ml(n);if(!r)throw new Error("image must be a PNG");let o=ee(t,e);await kt.writeFile(o.png,n),Cl(t,{v:e,source:"image",htmlRel:null,pngRel:St.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Oh(t){let e=await xe(),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 Dt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Mh(t){let e=await xe(),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=ee(r,o);await kt.rm(s.html,{force:!0}).catch(()=>{}),await kt.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await kt.rm(St.join(r.cwd,s),{force:!0}).catch(()=>{});return await te(),await Dt(r,"deleted"),oe("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Ch(t){let n=(await xe()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=St.join(bh,"mock-archive",t.id),o=Xo(t.cwd),s=St.join(r,".bostrat","mocks");await kt.mkdir(s,{recursive:!0});let a=await kt.readdir(o).catch(()=>[]);for(let i of a)await kt.copyFile(St.join(o,i),St.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await kt.stat(St.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,i.updatedAt=Date.now(),be(i)}return await te(),n.length}async function Nh(t){let e=await xe(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await te();for(let r of n)oe("mocks",r.id).catch(()=>{})}return n.length}async function Ph(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await xe();for(let r of n.mocks){await be(r);for(let o of r.versions||[])o.htmlRel&&await ke(r,o.v,"html"),(o.pngRel||o.posterRel)&&await ke(r,o.v,"png"),o.videoRel&&await ke(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}import Zo from"node:fs/promises";import Wt from"node:path";import H_ from"node:os";import W_ from"node:net";import{randomUUID as q_}from"node:crypto";import{execFile as K_}from"node:child_process";import{promisify as G_}from"node:util";var Se=G_(K_),V_=process.env.BOSTRAT_STATE_DIR||Wt.join(process.env.HOME||H_.tmpdir(),".bostrat"),Fh=Wt.join(V_,"views.json"),[Dh,Bh]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),yn=Number(process.env.BOSTRAT_VIEW_MAX)||3,z_="bostrat-view-",jr=t=>`${z_}${t}`,jh=new Set(["bostrat","www","api","views","app"]),La=t=>Object.assign(new Error(t),{status:400}),J_=new Set(["npm","pnpm","yarn","bun"]),Dl="package.json",X_=/[|;&`]|\$\(/,Y_=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Z_=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Uh=t=>String(t).trim().split(/\s+/).filter(Boolean),Q_=/^[A-Za-z_][A-Za-z0-9_]*=/;function tE(t){for(let e of Uh(t))if(!Q_.test(e))return e;return null}function Hh(t){let e=Uh(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function eE(t,e){let n=Hh(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Wt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Wt.resolve(t,n[r+1])}return t}var nE=t=>Zo.stat(t).then(e=>e.isDirectory(),()=>!1),Wh=t=>Zo.stat(t).then(e=>e.isFile(),()=>!1),Fa=t=>Zo.realpath(t).catch(()=>Wt.resolve(t));async function rE(t,e){if(Wt.resolve(t)===Wt.resolve(e))return!0;let[n,r]=await Promise.all([Fa(t),Fa(e)]);return n===r}async function oE(t){let n=(await Zo.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 Wh(Wt.join(t,o.name,Dl))?`./${o.name}`:null))).filter(Boolean)}async function sE(t,e){let[n,r]=await Promise.all([Fa(t),Fa(e)]),o=Wt.relative(n,r);if(o&&(o.startsWith("..")||Wt.isAbsolute(o)))throw La(`serve directory escapes the Track workspace: ${e}`)}async function aE(t,e){if(!await nE(t))throw La(`serve directory does not exist: ${t}`);let n=tE(e);if(!n)return;if(!n.includes("/")&&!await xp(n,{loginShell:!0,failOpen:!0}))throw La(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(X_.test(e)||Y_.has(n)||!J_.has(n)||!Hh(e).includes("run")||Z_.test(e))return;let r=eE(t,e);if(await Wh(Wt.join(r,Dl)))return;let o=await oE(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw La(`no ${Dl} in ${r}${s}`)}var ve=null,Nl=null;async function Fe(){return ve||(Nl||=(async()=>{try{ve=JSON.parse(await Zo.readFile(Fh,"utf8"))}catch{ve={views:[],adoptWatermarks:{}}}finally{Nl=null}return ve.views||=[],ve.adoptWatermarks||={},ve})(),Nl)}async function Te(){await Ft(Fh,ve)}function Le(t,e,n={}){return $({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 Yo=new Map,Ll=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,qh=12e3;function Fl(t){return Yo.get(t)||null}function Kh(t,{name:e,box:n}={}){let r=Yo.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 Yo.set(t,o),o}function Bl(t,e){let n=Yo.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ua(t,{error:e=null}={}){let n=Yo.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Gh(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 jl(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function iE(t,e){let n=jl(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!jh.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!jh.has(s))return s}}function Qo(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=W_.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 Ul(t){if(process.platform!=="darwin"){await Se("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Se("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{}}}function Vh(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function cE(t,e){let n=new Set(t.views.map(o=>o.port)),r=Vh();if(e){let o=Number(e);if(!Number.isInteger(o)||o<1024||o>65535)throw new Error("invalid port");if(r.has(o))throw new Error(`port ${o} is the node's own API port`);if(n.has(o))throw new Error(`port ${o} is already owned by a view`);return o}for(let o=Dh;o<=Bh;o++)if(!(n.has(o)||r.has(o))&&!await Qo(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Dh}-${Bh}`)}async function lE(t){try{let{stdout:e}=await Se(C,P("list-sessions","-F","#{session_name}"));return e.split(`
114
- `).some(n=>n.trim()===jr(t))}catch{return!1}}async function ts(t){try{await Se(C,P("kill-session","-t",jr(t)))}catch{}}async function Hl(t){if(!await Do())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=jr(t.slug),n=t.serveCwd??t.cwd,r=Wt.dirname(process.execPath);await Se(C,P("new-session","-d","-s",e,"-c",n)),await Se(C,P("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Se(C,P("send-keys","-t",e,"Enter")),await Se(C,P("send-keys","-t",e,"-l",t.cmd)),await Se(C,P("send-keys","-t",e,"Enter"))}async function Lh(t,e=12){try{let{stdout:n}=await Se(C,P("capture-pane","-t",jr(t),"-p"));return String(n||"").trimEnd().split(`
113
+ `)}`:e),this.name="MockRenderError"}},Yn=null,Pl=null;async function ve(){return Yn||(Pl||=(async()=>{try{Yn=JSON.parse(await St.readFile(Ih,"utf8"))}catch{Yn={mocks:[]}}finally{Pl=null}return Yn.mocks||=[],Yn})(),Pl)}async function ee(){await Ht(Ih,Yn)}var xe=t=>Bt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Se=(t,e,n)=>{let r=(t.versions||[]).find(s=>s.v===e),o=n==="mp4"||n==="webm"?Zo(t,r,"video"):r&&Zo(t,r,n)||(n==="html"?ne(t,e).html:null);return o?ln("mocks",t.id,e,n,o).catch(()=>{}):Promise.resolve()};function Oh(t,e){Se(t,e.v,e.videoRel?.endsWith(".webm")?"webm":"mp4"),e.posterRel&&Se(t,e.v,"png")}function jt(t,e,n={}){return I({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}:{},...t.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}var Qo=t=>At.join(t,".bostrat","mocks");function ne(t,e){let n=Qo(t.cwd);return{html:At.join(n,`${t.id}.v${e}.html`),png:At.join(n,`${t.id}.v${e}.png`)}}function Zo(t,e,n){if(!e)return null;let r=n==="html"?e.htmlRel:n==="png"?e.pngRel??e.posterRel??null:n==="video"?e.videoRel??null:null;return r?At.join(t.cwd,r):null}function ce(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 jl(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var Y_=Buffer.from([137,80,78,71,13,10,26,10]);function Wa(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(Y_)}function X_(t){return!Buffer.isBuffer(t)||t.length<32?null:t.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:t.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function Z_(t){let e=Buffer.alloc(24),n=await St.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function Q_({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 tE(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,h)=>{r.onload=m,r.onerror=()=>h(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,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let h=m*o*4,g=0;g<o;g++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(g<u&&(u=g),g>d&&(d=g),m<f&&(f=m),p=m);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},e.toString("base64"))}async function eE(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((f,d)=>{c.onload=f,c.onerror=()=>d(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 nE({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await U_.launch({executablePath:W_,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new ie("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:jr})).newPage();a.setDefaultTimeout(Lr),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Lr}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Lr});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await tE(a,i),u=l&&Q_({...l,pad:G_*jr,align:jr});u&&(i=await eE(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Rh)throw new ie(`capture produced a ${i.length}-byte image`);let c=jl(i);if(!c)throw new ie("output is not a PNG");return await St.writeFile(e,i),{width:Math.round(c.width/jr),height:Math.round(c.height/jr),dpr:jr,renderMs:Date.now()-r}}catch(s){throw s instanceof ie?s:new ie(/timeout/i.test(s?.message||"")?`render timed out after ${Lr}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function Ah({htmlPath:t,outPath:e,width:n}){let r=await St.mkdtemp(At.join(_h.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Bl(q_,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Lr,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new ie(c?.killed?`render timed out after ${Lr}ms`:"firefox exited non-zero",s)}let a=await St.stat(e).catch(()=>null);if(!a||a.size<Rh)throw new ie(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=jl(await Z_(e));if(!i)throw new ie("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await St.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function rE(t){if(H_==="firefox")return Ah(t);try{return await nE(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return Ah(t);throw e}}var oE=rE;var Dl=Promise.resolve();function Ch(t){return Dl=Dl.then(t,t),Dl}async function Mh(t,e){let n=await ve(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await ee(),await jt(r,"rendering",{version:e});try{let o=await Ha();if(o<V_)throw new ie(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=ne(r,e),a=await oE({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await St.rm(s.png,{force:!0}).catch(()=>{});return}Ll(r,{v:e,source:"html",htmlRel:At.relative(r.cwd,s.html),pngRel:At.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await ee(),await jt(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),xe(r),Se(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await ee(),await jt(r,"error",{version:e,error:r.error}),xe(r)}}}function Ll(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 sE({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?K_:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Xn({trackId:t,group:e}={}){let n=await ve(),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 Le(t){let n=(await ve()).mocks.find(r=>r.id===t);return n?{...n}:null}function Nh({html:t,image:e,video:n}){if([typeof t=="string"&&t.trim(),!!e,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(e&&!Wa(e))throw new Error("image must be a PNG");if(n){let o=X_(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>Th)throw new Error(`video must be ${Th/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function Ph({trackId:t,name:e,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=sE({trackId:t,name:e,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=Nh({html:n,image:r,video:o}),m=await ve(),h={id:j_().slice(0,8),trackId:t,box:c||null,agent:l||null,name:e.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:f,height:null,dpr:null,state:"queued",source:d?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await St.mkdir(Qo(s),{recursive:!0}),m.mocks.push(h),d?(await St.writeFile(ne(h,1).html,n),await ee(),await jt(h,"created"),xe(h),Se(h,1,"html"),Ch(()=>Mh(h.id,1))):o?(await Bh(h,1,o,p),await ee(),await jt(h,"created"),await jt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),xe(h),Oh(h,h.versions[0])):(await jh(h,1,r),await ee(),await jt(h,"created"),await jt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),xe(h),Se(h,1,"png")),{...h}}async function Dh(t,{html:e,image:n,video:r,width:o}={}){let a=(await ve()).mocks.find(u=>u.id===t);if(!a)throw new Error("mock not found");let i=typeof e=="string"&&e.trim(),c=Nh({html:e,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await St.mkdir(Qo(a.cwd),{recursive:!0}),i?(a.state="queued",await St.writeFile(ne(a,l).html,e),await ee(),await jt(a,"updated",{version:l}),xe(a),Se(a,l,"html"),Ch(()=>Mh(a.id,l))):r?(await Bh(a,l,r,c),await ee(),await jt(a,"updated",{version:l}),await jt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:ce(a)?.durationS??null}),xe(a),Oh(a,a.versions.find(u=>u.v===l))):(await jh(a,l,n),await ee(),await jt(a,"updated",{version:l}),await jt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),xe(a),Se(a,l,"png")),{...a}}async function aE(t){try{let{stdout:e}=await Bl(z_,["-v","error","-print_format","json","-show_format","-show_streams",t],{timeout:$h,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(e),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function iE(t,e){try{await Bl(J_,["-y","-v","error","-i",t,"-frames:v","1","-f","image2",e],{timeout:$h,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await St.stat(e).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Bh(t,e,n,r){let o=Qo(t.cwd),s=At.join(o,`${t.id}.v${e}.${r.ext}`),a=At.join(o,`${t.id}.v${e}.poster.png`);await St.writeFile(s,n);let i=await aE(s),c=await iE(s,a);Ll(t,{v:e,source:"video",htmlRel:null,pngRel:null,videoRel:At.relative(t.cwd,s),posterRel:c?At.relative(t.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(t.width=i.width)}async function jh(t,e,n){let r=jl(n);if(!r)throw new Error("image must be a PNG");let o=ne(t,e);await St.writeFile(o.png,n),Ll(t,{v:e,source:"image",htmlRel:null,pngRel:At.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Lh(t){let e=await ve(),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 jt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Fh(t){let e=await ve(),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=ne(r,o);await St.rm(s.html,{force:!0}).catch(()=>{}),await St.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await St.rm(At.join(r.cwd,s),{force:!0}).catch(()=>{});return await ee(),await jt(r,"deleted"),se("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Uh(t){let n=(await ve()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=At.join(Eh,"mock-archive",t.id),o=Qo(t.cwd),s=At.join(r,".bostrat","mocks");await St.mkdir(s,{recursive:!0});let a=await St.readdir(o).catch(()=>[]);for(let i of a)await St.copyFile(At.join(o,i),At.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await St.stat(At.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=t.name||t.id,i.updatedAt=Date.now(),xe(i)}return await ee(),n.length}async function Hh(t){let e=await ve(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await ee();for(let r of n)se("mocks",r.id).catch(()=>{})}return n.length}async function Wh(){if(!mt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await It("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await ve();for(let r of n.mocks){await xe(r);for(let o of r.versions||[])o.htmlRel&&await Se(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Se(r,o.v,"png"),o.videoRel&&await Se(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}import es from"node:fs/promises";import Kt from"node:path";import cE from"node:os";import lE from"node:net";import{randomUUID as uE}from"node:crypto";import{execFile as dE}from"node:child_process";import{promisify as fE}from"node:util";var Ae=fE(dE),pE=process.env.BOSTRAT_STATE_DIR||Kt.join(process.env.HOME||cE.tmpdir(),".bostrat"),zh=Kt.join(pE,"views.json"),[qh,Kh]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),bn=Number(process.env.BOSTRAT_VIEW_MAX)||3,mE="bostrat-view-",Fr=t=>`${mE}${t}`,Gh=new Set(["bostrat","www","api","views","app"]),qa=t=>Object.assign(new Error(t),{status:400}),hE=new Set(["npm","pnpm","yarn","bun"]),Hl="package.json",wE=/[|;&`]|\$\(/,gE=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),yE=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Jh=t=>String(t).trim().split(/\s+/).filter(Boolean),bE=/^[A-Za-z_][A-Za-z0-9_]*=/;function kE(t){for(let e of Jh(t))if(!bE.test(e))return e;return null}function Yh(t){let e=Jh(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function xE(t,e){let n=Yh(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Kt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Kt.resolve(t,n[r+1])}return t}var SE=t=>es.stat(t).then(e=>e.isDirectory(),()=>!1),Xh=t=>es.stat(t).then(e=>e.isFile(),()=>!1),Ka=t=>es.realpath(t).catch(()=>Kt.resolve(t));async function vE(t,e){if(Kt.resolve(t)===Kt.resolve(e))return!0;let[n,r]=await Promise.all([Ka(t),Ka(e)]);return n===r}async function TE(t){let n=(await es.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 Xh(Kt.join(t,o.name,Hl))?`./${o.name}`:null))).filter(Boolean)}async function AE(t,e){let[n,r]=await Promise.all([Ka(t),Ka(e)]),o=Kt.relative(n,r);if(o&&(o.startsWith("..")||Kt.isAbsolute(o)))throw qa(`serve directory escapes the Track workspace: ${e}`)}async function _E(t,e){if(!await SE(t))throw qa(`serve directory does not exist: ${t}`);let n=kE(e);if(!n)return;if(!n.includes("/")&&!await $p(n,{loginShell:!0,failOpen:!0}))throw qa(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(wE.test(e)||gE.has(n)||!hE.has(n)||!Yh(e).includes("run")||yE.test(e))return;let r=xE(t,e);if(await Xh(Kt.join(r,Hl)))return;let o=await TE(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw qa(`no ${Hl} in ${r}${s}`)}var Te=null,Fl=null;async function He(){return Te||(Fl||=(async()=>{try{Te=JSON.parse(await es.readFile(zh,"utf8"))}catch{Te={views:[],adoptWatermarks:{}}}finally{Fl=null}return Te.views||=[],Te.adoptWatermarks||={},Te})(),Fl)}async function _e(){await Ht(zh,Te)}function Ue(t,e,n={}){return I({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 ts=new Map,Kl=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Zh=12e3;function Gl(t){return ts.get(t)||null}function Qh(t,{name:e,box:n}={}){let r=ts.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 ts.set(t,o),o}function Wl(t,e){let n=ts.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ga(t,{error:e=null}={}){let n=ts.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function tw(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 ql(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function EE(t,e){let n=ql(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Gh.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Gh.has(s))return s}}function ns(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=lE.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 Vl(t){if(process.platform!=="darwin"){await Ae("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Ae("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{}}}function ew(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function IE(t,e){let n=new Set(t.views.map(o=>o.port)),r=ew();if(e){let o=Number(e);if(!Number.isInteger(o)||o<1024||o>65535)throw new Error("invalid port");if(r.has(o))throw new Error(`port ${o} is the node's own API port`);if(n.has(o))throw new Error(`port ${o} is already owned by a view`);return o}for(let o=qh;o<=Kh;o++)if(!(n.has(o)||r.has(o))&&!await ns(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${qh}-${Kh}`)}async function RE(t){try{let{stdout:e}=await Ae(M,D("list-sessions","-F","#{session_name}"));return e.split(`
114
+ `).some(n=>n.trim()===Fr(t))}catch{return!1}}async function rs(t){try{await Ae(M,D("kill-session","-t",Fr(t)))}catch{}}async function zl(t){if(!await Lo())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=Fr(t.slug),n=t.serveCwd??t.cwd,r=Kt.dirname(process.execPath);await Ae(M,D("new-session","-d","-s",e,"-c",n)),await Ae(M,D("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Ae(M,D("send-keys","-t",e,"Enter")),await Ae(M,D("send-keys","-t",e,"-l",t.cmd)),await Ae(M,D("send-keys","-t",e,"Enter"))}async function Vh(t,e=12){try{let{stdout:n}=await Ae(M,D("capture-pane","-t",Fr(t),"-p"));return String(n||"").trimEnd().split(`
115
115
  `).filter(Boolean).slice(-e).join(`
116
- `)}catch{return""}}var je=new Map,uE=12e4;async function dE(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await lE(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Lh(t.slug)||"server exited before listening"),je.delete(t.id),await Le(t,t.state,t.error?{error:t.error}:{}),!0}if(await Qo(t.port))return je.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await Le(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>uE?(t.state="error",t.error=await Lh(t.slug)||"never started listening",await Le(t,"error",{error:t.error}),!0):!1;let o=(je.get(t.id)||0)+1;return je.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var Pl=!1;async function Ha(){if(!Pl){Pl=!0;try{let t=await Fe(),e=!1;for(let n of t.views)await dE(n)&&(e=!0);e&&await Te()}finally{Pl=!1}}}async function Wa({trackId:t}={}){await Ha();let e=await Fe();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function zh(t){let n=(await Fe()).views.find(r=>r.id===t);return n?{...n}:null}function Wl(t){return ve&&ve.views.find(e=>e.slug===t)||null}async function ql({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=Wt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await sE(r,u),await aE(u,n)},d=await Fe();if(i!=="detected"){let h=jl(e),y=String(n).trim(),k=d.views.find(v=>v.trackId===t&&(v.slug===h||jl(v.name)===h||v.cmd===y&&Wt.resolve(v.serveCwd??v.cwd)===u));if(k)return fE(d,k,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(h=>h.state==="live"||h.state==="starting").length>=yn)throw new Error(`view cap reached (${yn} live) \u2014 stop one first`);let m={id:q_().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:iE(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await cE(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Hl(m),d.views.push(m),await Te(),await Le(m,"created",{origin:i}),Bl(t,m.id),i==="detected"&&await Qo(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await Te(),await Le(m,"live")),{...m}}async function fE(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 rE(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Qo(e.port))return await Te(),Bl(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(f=>f.id!==e.id&&(f.state==="live"||f.state==="starting")).length>=yn)throw new Error(`view cap reached (${yn} live) \u2014 stop one first`);return e.origin==="detected"?(await Ul(e.port),e.origin="agent"):await ts(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),je.delete(e.id),await Hl(e),await Te(),await Le(e,"created",{restarted:!0,reused:!0}),Bl(e.trackId,e.id),{...e,reused:!0}}async function Jh(t){let n=(await Fe()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await Ul(n.port):await ts(n.slug),n.state!=="stopped"&&(n.state="stopped",je.delete(n.id),await Te(),await Le(n,"stopped")),{...n}}async function Xh(t){let e=await Fe(),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>=yn)throw new Error(`view cap reached (${yn} live) \u2014 stop one first`);return await ts(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),je.delete(n.id),await Hl(n),await Te(),await Le(n,"created",{restarted:!0}),{...n}}async function Yh(t){let e=await Fe(),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 Ul(r.port):await ts(r.slug),e.views.splice(n,1),je.delete(r.id),await Te(),await Le(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function Zh(t){let e=await Fe(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await ts(r.slug),je.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await Te()),n.length}var pE=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function Qh(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await Fe(),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 Te()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(pE)){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(!Vh().has(c)&&!n.views.some(l=>l.port===c)&&await Qo(c))try{i.push(await ql({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 We from"node:fs/promises";import qe from"node:path";import mE from"node:os";import{randomUUID as hE}from"node:crypto";var Yn=process.env.BOSTRAT_STATE_DIR||qe.join(process.env.HOME||mE.tmpdir(),".bostrat"),ew=qe.join(Yn,"runbooks.json"),qa=qe.join(Yn,"runbooks"),tw=256*1024,wE=[{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 es(t){return wE.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Ue=null,Kl=null;async function Ke(){return Ue||(Kl||=(async()=>{let t=!0;try{Ue=JSON.parse(await We.readFile(ew,"utf8"))}catch{Ue={runbooks:[]},t=!1}finally{Kl=null}return Ue.runbooks||=[],!t&&ht()&&await gE().catch(()=>{}),Ue})(),Kl)}async function Zn(){await Ft(ew,Ue)}var ns=t=>Promise.resolve(t).catch(()=>{});function Ka(t){return ns(Pt("runbooks",{...t},{trackId:t.trackId}))}async function gE(){let{records:t,source:e}=await $t("runbooks");if(!(e!=="plane"||!t.length)){await We.mkdir(qa,{recursive:!0});for(let n of t){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await da("runbooks",r.id,o.v,"md");typeof s=="string"&&await We.writeFile(He(r,o.v),s).catch(()=>{})}Ue.runbooks.push(r)}Ue.runbooks.length&&await Zn()}}function Lr(t,e,n={}){return $({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 He(t,e){return qe.join(qa,`${t.id}.v${e}.md`)}function nw(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 rw(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>tw)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${tw/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=es(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 yE(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Ga({trackId:t,status:e}={}){let n=await Ke(),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 Gl(t){let n=(await Ke()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function ow({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=rw(r),c=await Ke(),l={id:hE().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 We.mkdir(qa,{recursive:!0}),await We.writeFile(He(l,1),r),l.versions.push({v:1,mdRel:qe.relative(Yn,He(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Zn(),await Lr(l,"created",{version:1}),Ka(l),ns(cn("runbooks",l.id,1,"md",He(l,1))),{...l}}async function sw(t,{md:e,note:n}={}){let o=(await Ke()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");yE(o,"update");let s=rw(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await We.mkdir(qa,{recursive:!0}),await We.writeFile(He(o,a),e),o.versions.push({v:a,mdRel:qe.relative(Yn,He(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Zn(),await Lr(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Ka(o),ns(cn("runbooks",o.id,a,"md",He(o,a))),{...o}}async function aw(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Ke()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Zn(),await Lr(r,e==="done"?"done":"reopened"),Ka(r),{...r})}async function iw(t){let e=await Ke(),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 We.rm(qe.join(Yn,o.mdRel),{force:!0}).catch(()=>{});return await Zn(),r.trackDeletedAt||await Lr(r,"deleted"),ns(oe("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function cw(t){let n=(await Ke()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Lr(r,"orphaned"),r.cwd=null,Ka(r);return n.length&&await Zn(),n.length}async function lw(t){let e=await Ke(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Lr(r,"deleted");for(let o of r.versions||[])await We.rm(qe.join(Yn,o.mdRel),{force:!0}).catch(()=>{});ns(oe("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Zn()),n.length}async function uw(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Ke();for(let r of n.runbooks){await Pt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await cn("runbooks",r.id,o.v,"md",qe.join(Yn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import ss from"node:fs/promises";import Tw from"node:path";import zE from"node:os";import{randomUUID as Aw}from"node:crypto";import bE from"node:fs/promises";import dw from"node:path";import kE from"node:os";var fw=()=>dw.join(process.env.BOSTRAT_STATE_DIR||dw.join(process.env.HOME||kE.tmpdir(),".bostrat"),"integrations.json");async function Ge(){try{return JSON.parse(await bE.readFile(fw(),"utf8"))||{}}catch{return{}}}var Va=["linked","prOpened","prMerged","create"],Vl=["prompt","auto","off"],xE={create:["prompt","off"]},pw=t=>xE[t]??Vl;function za(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of Va){let s=pw(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 Qn(){let t=await Ge(),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:za(t),defaultTeam:n}:null}async function Ja(){let t=await Ge(),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:za(t)}}async function zl(){return za(await Ge())}async function mw(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${Va.join(", ")}`);for(let[r,o]of e){if(!Va.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Va.join(", ")}`);let s=pw(r);if(!s.includes(o))throw new Error(s.length<Vl.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Vl.join(", ")}`)}let n=await Ge();return n.ticketFlow={...za(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Ft(fw(),n,{mode:384}),n.ticketFlow}async function rs(){let t=(await Ge()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function vE(){let t=(await Ge()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var SE={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Xa(){let e=(await Ge()).voiceChat||{},n=e.provider||"openai",r=SE[n];if(!r)return null;let o=n==="openai"?await vE():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function hw(){let t=(await Ge()).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 tr(){let t=(await Ge()).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}}var Xl={};Ef(Xl,{createComment:()=>ME,findIssueUrl:()=>EE,issueStates:()=>PE,mdToAdf:()=>yw,moveIssue:()=>DE,parseIssueRef:()=>_E,resolveIssue:()=>IE,uploadImage:()=>CE,upsertCard:()=>RE,whoami:()=>Jl});var Fr=t=>String(t.baseUrl||"").replace(/\/$/,""),gw=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function er(t,e,n,r){let o=await fetch(`${Fr(t)}${n}`,{method:e,headers:{authorization:gw(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 TE=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,AE=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function _E(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(TE);return r?r[1].toUpperCase():null}let n=e.match(AE);return n?n[1]:null}function EE(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 IE(t,e){let n;try{n=await er(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:`${Fr(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function RE(t,e,{url:n,title:r,subtitle:o}){await er(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 $E=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function OE(t){let e=[],n=0;for(let r of t.matchAll($E)){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 yw(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
117
- `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...OE(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 ME(t,e,n){let r=await er(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:yw(n)});return r?.id?`${Fr(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function Jl(t){let e=await er(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Fr(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function CE(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(`${Fr(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:gw(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??`${Fr(t)}/rest/api/3/attachment/content/${c.id}`}var NE={new:"unstarted",indeterminate:"started",done:"completed"},ww=t=>NE[t?.key]??"started";async function PE(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([er(t,"GET",`/rest/api/3/issue/${n}?fields=status`),er(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:ww(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:ww(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function DE(t,e,n){await er(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var tu={};Ef(tu,{createComment:()=>qE,createIssue:()=>Ql,createProject:()=>Zl,findIssueUrl:()=>UE,issueStates:()=>GE,moveIssue:()=>VE,parseIssueRef:()=>FE,resolveIssue:()=>HE,teams:()=>Ur,uploadImage:()=>KE,upsertCard:()=>WE,whoami:()=>Yl});var BE=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Ae(t,e,n={}){let r=await fetch(BE(),{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){let s=o.errors[0],a=(s.extensions?.validationErrors??[]).flatMap(i=>Object.values(i?.constraints??{})).join("; ");throw new Error(`linear: ${s.message}${a?` \u2014 ${a}`:""}`)}return o?.data??{}}var jE=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,LE=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function FE(t){let e=String(t||"").trim(),n=e.match(jE);if(n)return n[1].toUpperCase();let r=e.match(LE);return r?r[1]:null}function UE(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 HE(t,e){let r=(await Ae(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 WE(t,e,{url:n,title:r,subtitle:o}){let s=await Ae(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 qE(t,e,n){let r=await Ae(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 Yl(t){let e=await Ae(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 KE(t,{filename:e,contentType:n,buffer:r}){let o=await Ae(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
116
+ `)}catch{return""}}var Fe=new Map,$E=12e4;async function OE(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await RE(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Vh(t.slug)||"server exited before listening"),Fe.delete(t.id),await Ue(t,t.state,t.error?{error:t.error}:{}),!0}if(await ns(t.port))return Fe.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await Ue(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>$E?(t.state="error",t.error=await Vh(t.slug)||"never started listening",await Ue(t,"error",{error:t.error}),!0):!1;let o=(Fe.get(t.id)||0)+1;return Fe.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var Ul=!1;async function Va(){if(!Ul){Ul=!0;try{let t=await He(),e=!1;for(let n of t.views)await OE(n)&&(e=!0);e&&await _e()}finally{Ul=!1}}}async function os({trackId:t}={}){await Va();let e=await He();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function nw(t){let n=(await He()).views.find(r=>r.id===t);return n?{...n}:null}function Jl(t){return Te&&Te.views.find(e=>e.slug===t)||null}async function Yl({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=Kt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await AE(r,u),await _E(u,n)},d=await He();if(i!=="detected"){let h=ql(e),g=String(n).trim(),k=d.views.find(S=>S.trackId===t&&(S.slug===h||ql(S.name)===h||S.cmd===g&&Kt.resolve(S.serveCwd??S.cwd)===u));if(k)return CE(d,k,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(h=>h.state==="live"||h.state==="starting").length>=bn)throw new Error(`view cap reached (${bn} live) \u2014 stop one first`);let m={id:uE().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:EE(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await IE(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await zl(m),d.views.push(m),await _e(),await Ue(m,"created",{origin:i}),Wl(t,m.id),i==="detected"&&await ns(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await _e(),await Ue(m,"live")),{...m}}async function CE(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 vE(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await ns(e.port))return await _e(),Wl(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(f=>f.id!==e.id&&(f.state==="live"||f.state==="starting")).length>=bn)throw new Error(`view cap reached (${bn} live) \u2014 stop one first`);return e.origin==="detected"?(await Vl(e.port),e.origin="agent"):await rs(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Fe.delete(e.id),await zl(e),await _e(),await Ue(e,"created",{restarted:!0,reused:!0}),Wl(e.trackId,e.id),{...e,reused:!0}}async function rw(t){let n=(await He()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await Vl(n.port):await rs(n.slug),n.state!=="stopped"&&(n.state="stopped",Fe.delete(n.id),await _e(),await Ue(n,"stopped")),{...n}}async function ow(t){let e=await He(),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>=bn)throw new Error(`view cap reached (${bn} live) \u2014 stop one first`);return await rs(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Fe.delete(n.id),await zl(n),await _e(),await Ue(n,"created",{restarted:!0}),{...n}}async function sw(t){let e=await He(),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 Vl(r.port):await rs(r.slug),e.views.splice(n,1),Fe.delete(r.id),await _e(),await Ue(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function aw(t){let e=await He(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await rs(r.slug),Fe.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await _e()),n.length}var ME=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function iw(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await He(),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 _e()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(ME)){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(!ew().has(c)&&!n.views.some(l=>l.port===c)&&await ns(c))try{i.push(await Yl({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 Ke from"node:fs/promises";import Ge from"node:path";import NE from"node:os";import{randomUUID as PE}from"node:crypto";var Zn=process.env.BOSTRAT_STATE_DIR||Ge.join(process.env.HOME||NE.tmpdir(),".bostrat"),lw=Ge.join(Zn,"runbooks.json"),za=Ge.join(Zn,"runbooks"),cw=256*1024,DE=[{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 ss(t){return DE.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var We=null,Xl=null;async function Ee(){return We||(Xl||=(async()=>{let t=!0;try{We=JSON.parse(await Ke.readFile(lw,"utf8"))}catch{We={runbooks:[]},t=!1}finally{Xl=null}return We.runbooks||=[],!t&&mt()&&await BE().catch(()=>{}),We})(),Xl)}async function Qn(){await Ht(lw,We)}var as=t=>Promise.resolve(t).catch(()=>{});function Ja(t){return as(Bt("runbooks",{...t},{trackId:t.trackId}))}async function BE(){let{records:t,source:e}=await It("runbooks");if(!(e!=="plane"||!t.length)){await Ke.mkdir(za,{recursive:!0});for(let n of t){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await Bo("runbooks",r.id,o.v,"md");typeof s=="string"&&await Ke.writeFile(qe(r,o.v),s).catch(()=>{})}We.runbooks.push(r)}We.runbooks.length&&await Qn()}}function Ur(t,e,n={}){return I({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 qe(t,e){return Ge.join(za,`${t.id}.v${e}.md`)}function uw(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 dw(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>cw)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${cw/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=ss(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 jE(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Zl({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 fw({trackId:t,status:e}={}){let n=await Ee(),r=await Zl({trackId:t,status:e});if(!mt())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await It("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!t||c.trackId===t)&&(!e||c.status===e));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Ql(t){let e=await LE(t);if(e)return e;if(!mt())return null;let{records:n,source:r}=await It("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===t);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function pw(t,e){let n=await Bo("runbooks",t.id,e,"md");return typeof n=="string"?n:null}async function LE(t){let n=(await Ee()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function mw({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=dw(r),c=await Ee(),l={id:PE().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 Ke.mkdir(za,{recursive:!0}),await Ke.writeFile(qe(l,1),r),l.versions.push({v:1,mdRel:Ge.relative(Zn,qe(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Qn(),await Ur(l,"created",{version:1}),Ja(l),as(ln("runbooks",l.id,1,"md",qe(l,1))),{...l}}async function hw(t,{md:e,note:n}={}){let o=(await Ee()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");jE(o,"update");let s=dw(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Ke.mkdir(za,{recursive:!0}),await Ke.writeFile(qe(o,a),e),o.versions.push({v:a,mdRel:Ge.relative(Zn,qe(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Qn(),await Ur(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Ja(o),as(ln("runbooks",o.id,a,"md",qe(o,a))),{...o}}async function ww(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 r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Qn(),await Ur(r,e==="done"?"done":"reopened"),Ja(r),{...r})}async function gw(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 Ke.rm(Ge.join(Zn,o.mdRel),{force:!0}).catch(()=>{});return await Qn(),r.trackDeletedAt||await Ur(r,"deleted"),as(se("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function yw(t){let n=(await Ee()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Ur(r,"orphaned"),r.cwd=null,Ja(r);return n.length&&await Qn(),n.length}async function bw(t){let e=await Ee(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Ur(r,"deleted");for(let o of r.versions||[])await Ke.rm(Ge.join(Zn,o.mdRel),{force:!0}).catch(()=>{});as(se("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Qn()),n.length}async function kw(){if(!mt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await It("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Ee();for(let r of n.runbooks){await Bt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await ln("runbooks",r.id,o.v,"md",Ge.join(Zn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import ls from"node:fs/promises";import Nw from"node:path";import hI from"node:os";import{randomUUID as Pw}from"node:crypto";import FE from"node:fs/promises";import xw from"node:path";import UE from"node:os";var Sw=()=>xw.join(process.env.BOSTRAT_STATE_DIR||xw.join(process.env.HOME||UE.tmpdir(),".bostrat"),"integrations.json");async function Ve(){try{return JSON.parse(await FE.readFile(Sw(),"utf8"))||{}}catch{return{}}}var Ya=["linked","prOpened","prMerged","create"],tu=["prompt","auto","off"],HE={create:["prompt","off"]},vw=t=>HE[t]??tu;function Xa(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of Ya){let s=vw(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 tr(){let t=await Ve(),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:Xa(t),defaultTeam:n}:null}async function Za(){let t=await Ve(),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:Xa(t)}}async function eu(){return Xa(await Ve())}async function Tw(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${Ya.join(", ")}`);for(let[r,o]of e){if(!Ya.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Ya.join(", ")}`);let s=vw(r);if(!s.includes(o))throw new Error(s.length<tu.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${tu.join(", ")}`)}let n=await Ve();return n.ticketFlow={...Xa(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Ht(Sw(),n,{mode:384}),n.ticketFlow}async function is(){let t=(await Ve()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function WE(){let t=(await Ve()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var qE={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Qa(){let e=(await Ve()).voiceChat||{},n=e.provider||"openai",r=qE[n];if(!r)return null;let o=n==="openai"?await WE():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function Aw(){let t=(await Ve()).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 er(){let t=(await Ve()).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}}var ru={};Df(ru,{createComment:()=>QE,findIssueUrl:()=>zE,issueStates:()=>nI,mdToAdf:()=>Iw,moveIssue:()=>rI,parseIssueRef:()=>VE,resolveIssue:()=>JE,uploadImage:()=>tI,upsertCard:()=>YE,whoami:()=>nu});var Hr=t=>String(t.baseUrl||"").replace(/\/$/,""),Ew=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function nr(t,e,n,r){let o=await fetch(`${Hr(t)}${n}`,{method:e,headers:{authorization:Ew(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 KE=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,GE=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function VE(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(KE);return r?r[1].toUpperCase():null}let n=e.match(GE);return n?n[1]:null}function zE(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 JE(t,e){let n;try{n=await nr(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:`${Hr(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function YE(t,e,{url:n,title:r,subtitle:o}){await nr(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 XE=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function ZE(t){let e=[],n=0;for(let r of t.matchAll(XE)){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 Iw(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
117
+ `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...ZE(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 QE(t,e,n){let r=await nr(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Iw(n)});return r?.id?`${Hr(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function nu(t){let e=await nr(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Hr(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function tI(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(`${Hr(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:Ew(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??`${Hr(t)}/rest/api/3/attachment/content/${c.id}`}var eI={new:"unstarted",indeterminate:"started",done:"completed"},_w=t=>eI[t?.key]??"started";async function nI(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([nr(t,"GET",`/rest/api/3/issue/${n}?fields=status`),nr(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:_w(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:_w(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function rI(t,e,n){await nr(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var iu={};Df(iu,{createComment:()=>dI,createIssue:()=>au,createProject:()=>su,findIssueUrl:()=>cI,issueStates:()=>pI,moveIssue:()=>mI,parseIssueRef:()=>iI,resolveIssue:()=>lI,teams:()=>Wr,uploadImage:()=>fI,upsertCard:()=>uI,whoami:()=>ou});var oI=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Ie(t,e,n={}){let r=await fetch(oI(),{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){let s=o.errors[0],a=(s.extensions?.validationErrors??[]).flatMap(i=>Object.values(i?.constraints??{})).join("; ");throw new Error(`linear: ${s.message}${a?` \u2014 ${a}`:""}`)}return o?.data??{}}var sI=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,aI=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function iI(t){let e=String(t||"").trim(),n=e.match(sI);if(n)return n[1].toUpperCase();let r=e.match(aI);return r?r[1]:null}function cI(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 lI(t,e){let r=(await Ie(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 uI(t,e,{url:n,title:r,subtitle:o}){let s=await Ie(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 dI(t,e,n){let r=await Ie(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 ou(t){let e=await Ie(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 fI(t,{filename:e,contentType:n,buffer:r}){let o=await Ie(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
118
118
  fileUpload(contentType: $contentType, filename: $filename, size: $size) {
119
119
  success
120
120
  uploadFile { uploadUrl assetUrl headers { key value } }
121
121
  }
122
- }`,{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 GE(t,e){let n=await Ae(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 VE(t,e,n){if(!(await Ae(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 Ur(t){return((await Ae(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var bw=255;async function Zl(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>bw,a=s?`${o.slice(0,bw-1).trimEnd()}\u2026`:o,i=await Ae(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function Ql(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Ae(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 _w={linear:{api:tu,config:Qn},jira:{api:Xl,config:Ja}};async function iu(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(_w)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function nr(t){let e=_w[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function cu(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 Ew=()=>process.env.BOSTRAT_STATE_DIR||Tw.join(process.env.HOME||zE.tmpdir(),".bostrat"),ou=()=>Tw.join(Ew(),"tickets.json"),Iw=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Ya=t=>`${Iw()}/track/${t}`,bn=null,eu=null,nu=Promise.resolve();async function is(){return bn||(eu??=ss.readFile(ou(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(bn=t,!e&&ht()){let{records:n,source:r}=await $t("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;bn[s]=i}Object.keys(bn).length&&await su().catch(()=>{})}}return bn}).finally(()=>eu=null),eu)}async function su(){let t=JSON.stringify({tickets:bn},null,2),e=`${ou()}.${process.pid}.tmp`;await ss.mkdir(Ew(),{recursive:!0}),await ss.writeFile(e,t),await ss.rename(e,ou())}async function kn(t=null){if(nu=nu.then(su,su),await nu.catch(()=>{}),!t)return;let e=bn?.[t];e?Pt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):oe("tickets",t).catch(()=>{})}async function _e(t){return(await is())[t]??null}async function Za(){return{...await is()}}async function Rw(){return(await iu()).length>0}async function Hr(t,e,{source:n="api"}={}){let r=await iu();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 f=u.api.parseIssueRef(e);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}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,...t.workspace?.kind==="multi"?{repos:(t.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await is())[t.id]=c,await kn(t.id),await $({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 as(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await os(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Mw(t.id,c,"linked"),c}async function lu(t){let e=await is();return e[t]?(delete e[t],await kn(t),!0):!1}var au=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function $w(t,e){if(!t.track?.cwd)return null;let n=ye(await B({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function JE(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${au(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${au(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function XE(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.track.repos?.length&&n.push(t.track.repos.join(" + ")),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(au(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function as(t,e){let n=await nr(e);if(!n)return;let r=await $w(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:Ya(t),title:`Bostrat Track: ${e.track.name}`,subtitle:XE(e,r)})}function Qa(t){let e=new URL(Iw());return`${e.protocol}//${t}.${e.host}/`}var YE={linked:"linked",opened:"prOpened",merged:"prMerged"};function ZE(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 Ow(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await kn(t),await $({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 Mw(t,e,n){let r=await nr(e);if(!r)return;let o=r.cfg.ticketFlow?.[YE[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=cu(s,a),c=ZE(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 Ow(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await $({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Aw(),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 Cw(t){let e=await _e(t);if(!e)throw new Error("no ticket linked to this Track");let n=await nr(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=cu(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await kn(t)),{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 uu(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await _e(t);if(!r)throw new Error("no ticket linked to this Track");let o=await nr(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=cu(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 kn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Ow(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Nw(t,e){let n=await _e(t);if(!n)throw new Error("no ticket linked to this Track");let r=await nr(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 $({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 Pw(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 uu(t.id,o.payload.to?.id,{mode:"offer"})),await $({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 kw=50;async function Dw(){let t=await Qn();if(!t)throw new Error("no ticketing integration configured");return Ur(t)}function QE(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>kw)throw new Error(`too many issues (${n.length}); cap is ${kw} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function tI(t,e,n,r){let o=r??await Ur(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 _e(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 Bw(t,e){let n=await Qn();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=QE(e),o=await Ur(n),s=await tI(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Aw();return await $({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 jw(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 $({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 Qn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Ur(a)).find(h=>h.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=[],f=null;if(i.epic){let m=await Zl(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let h=await Ql(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:m.title,message:h?.message||String(h)})}await $({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 $({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 d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await _e(t.id)&&(p=await Hr(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function os(t,e){let n=await nr(t);n&&await n.api.createComment(n.cfg,t,e)}async function eI(t,e,n){let r=await nr(t);if(!r)return null;let o=await Be(e);if(!o)return null;let s=ae(o,n);if(!s)return null;let a=await ss.readFile(ee(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 xw=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),ru=new Map;function nI(t,e){if(xw()<=0)return as(t,e);if(ru.has(t))return;let n=setTimeout(()=>{ru.delete(t),_e(t).then(r=>r?as(t,r):null).catch(()=>{})},xw());n.unref?.(),ru.set(t,n)}var vw=new Map;async function Sw(t,e,n){let r=vw.get(t)??0;Date.now()-r<600*1e3||(vw.set(t,Date.now()),await $({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function rI(t,{resolveTrack:e=null}={}){let n=await _e(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await iu())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 Hr(s,o,{source:"chat"})}catch(a){await Sw(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 kn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await kn(t.trackId),await as(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 kn(t.trackId),await as(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await $w(n,t.trackId).catch(()=>null):null;await os(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
123
-
124
- Agent cost for this Track: ${JE(o)}.`:"")),await Mw(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await eI(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await os(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
125
-
126
- ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await os(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 os(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Qa(r.slug)}
127
- _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await nI(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Sw(t.trackId,s?.cwd??null,o)}}function Lw({resolveTrack:t}){Ht.on("event",e=>{rI(e,{resolveTrack:t}).catch(()=>{})})}async function Fw(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await is(),r=0;for(let[o,s]of Object.entries(n))await Pt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import oI from"node:crypto";import ti from"node:fs/promises";import Uw from"node:path";import sI from"node:os";var Hw=process.env.BOSTRAT_STATE_DIR||Uw.join(process.env.HOME||sI.tmpdir(),".bostrat"),pu=Uw.join(Hw,"pr-links.json"),K=(t,e)=>`${t}#${Number(e)}`,Ww=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=oI.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},xn=null,du=null,fu=Promise.resolve();async function xt(){return xn||(du??=ti.readFile(pu,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(xn=t,!e&&ht()){let{records:n,source:r}=await $t("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;xn[K(i.repo,i.number)]=i}Object.keys(xn).length&&await mu().catch(()=>{})}}return xn}).finally(()=>du=null),du)}async function mu(){let t=JSON.stringify({links:xn},null,2),e=`${pu}.${process.pid}.tmp`;await ti.mkdir(Hw,{recursive:!0}),await ti.writeFile(e,t),await ti.rename(e,pu)}async function It(t){fu=fu.then(mu,mu),await fu.catch(()=>{});let e=xn?.[t];e&&Pt("prlinks",{id:Ww(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var aI=new Set(["api","observed","reconciled"]);async function Jt({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=K(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:aI.has(a),firstSeen:Date.now()},await It(K(t,e)),{link:i[c],created:!0})}async function cs(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||n.announced?null:(n.announced=!0,await It(K(t,e)),n)}async function Bt(t,e){return!t||e==null?null:(await xt())[K(t,e)]??null}async function vn(t,e,n){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await It(K(t,e)),r):null}async function qw(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[K(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await It(K(t,e)),o):null}async function Kw(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[K(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await It(K(t,e)),o):null}async function ei(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await It(K(t,e)),r)}async function ni(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await It(K(t,e)),n)}async function ri(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await It(K(t,e)),r)}async function oi(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await It(K(t,e)),n)}async function Gw(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(r.baseFoldAt=n,await It(K(t,e)),r):null}async function Vw(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await It(K(t,e)),n)}async function zw(t,e,n){if(!t||e==null||!n)return null;let r=(await xt())[K(t,e)];return r?(r.lastHeadSha=n,await It(K(t,e)),r):null}async function ls(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await It(K(t,e)),r):null}async function rr(t,e,n=!0){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await It(K(t,e)),r):null}async function us(t,e,n=!0){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await It(K(t,e)),r):null}async function Jw(t,e,n){if(!t||e==null)return null;let r=(await xt())[K(t,e)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await It(K(t,e)),r}async function ds(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await It(K(t,e)),n)}var iI=new Set(["merged","closed","lost"]);async function Xw(){return Object.values(await xt()).filter(t=>!iI.has(t.state))}async function Ee(){return Object.values(await xt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function Yw(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await xt(),r=0;for(let[o,s]of Object.entries(n))await Pt("prlinks",{id:Ww(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Qw=t=>Math.round(t*100)/100;function cI(t){return!t||t.kind==="setup"?[]:qt(t.workspace).filter(e=>e&&(e.repo||e.base)).map(e=>({key:e.repo||e.base,slug:e.repo??null,base:e.base??null,name:e.repo?e.repo.split("/").pop():Zw.basename(e.base)}))}function tg(t){let e=new Map;for(let n of t||[])for(let r of cI(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function si(t){let e=t.events?.length?t.events[t.events.length-1].at:0;return Math.max(t.createdAt||0,t.archivedAt||0,t.lastResponseAt||0,e||0)||null}var hu=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function lI(t){if(!Number.isFinite(t)||t<=0)return null;let e=t/864e5;if(e>=1)return`${Math.round(e)}d`;let n=t/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(t/6e4))}m`}function wu(t,e=[]){let n=[],r=t?.events||[],o=new Map,s=0;for(let u of e){if(u.type!=="pr.state.changed")continue;let f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,p]=f.split("#");return`${d.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(hu(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(hu(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${hu(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=ye(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?lI(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function uI(t){let e=[],n=[...t?.boxes||[]].sort((o,s)=>(o.createdAt||0)-(s.createdAt||0));for(let o of n){if(o.role!=="lead"&&o.role!=="handed-off")continue;let s=Array.isArray(o.epochs)&&o.epochs.length?o.epochs.map(a=>a.agent):[o.agent];for(let a of s)a&&e[e.length-1]!==a&&e.push(a)}let r=new Set(n.filter(o=>o.role==="subtask"&&o.agent&&!e.includes(o.agent)).map(o=>o.agent));return{lead:e,subtask:[...r]}}function eg(t,e,n){let r=new Set(t.tracks.map(d=>d.id)),o=0,s=0,a=0,i=null,c=null,l=0;for(let d of t.tracks){d.archivedAt?s++:o++,d.beat?.text&&a++;let p=si(d);p&&(!i||p>i)&&(i=p),d.createdAt&&(!c||d.createdAt<c)&&(c=d.createdAt),l+=e.get(d.id)||0}let u=0,f=0;for(let d of n)r.has(d.trackId)&&(t.slug&&d.repo!==t.slug||t.slug&&(d.state==="merged"?f++:d.state!=="closed"&&u++));return{key:t.key,slug:t.slug,base:t.base,name:t.name,tracks:{total:t.tracks.length,live:o,archived:s,withBeat:a},prs:{open:u,merged:f},usd:Qw(l),firstActivityAt:c,lastActivityAt:i}}async function ng(t){let e=tg(t),n=await Ee().catch(()=>[]),r=new Map,o=new Set;for(let a of e.values())for(let i of a.tracks){if(o.has(i.id))continue;o.add(i.id);let c=await B(i).catch(()=>[]);r.set(i.id,ye(c).totalUsd)}let s=[...e.values()].map(a=>eg(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function rg(t,e){let r=tg(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([Ee().catch(()=>[]),Xn().catch(()=>[]),Wa().catch(()=>[]),Ga().catch(()=>[]),Za().catch(()=>({}))]),l=new Map,u=[],f=(d,p)=>(si(p)||p.createdAt||0)-(si(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let p=await B(d).catch(()=>[]),m=ye(p);l.set(d.id,m.totalUsd);let h=c[d.id]||null;u.push({trackId:d.id,name:d.name,createdAt:d.createdAt??null,archivedAt:d.archivedAt??null,live:!d.archivedAt,beat:d.beat??null,agents:uI(d),handoffs:(d.events||[]).filter(y=>y.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:qt(d.workspace).map(y=>y.repo||Zw.basename(y.base||"")),prs:o.filter(y=>y.trackId===d.id&&(!r.slug||y.repo===r.slug)).map(y=>({repo:y.repo,number:y.number,state:y.state??null,title:y.title??null,url:y.url??null})),mocks:s.filter(y=>y.trackId===d.id).length,views:a.filter(y=>y.trackId===d.id).length,runbooks:i.filter(y=>y.trackId===d.id).map(y=>({name:y.name,status:y.status})),ticket:h?{provider:h.provider,key:h.key,url:h.url??null}:null,usd:Qw(m.totalUsd),lastActivityAt:si(d)})}return{...eg(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as dI}from"node:child_process";import{promisify as fI}from"node:util";import{randomUUID as pI}from"node:crypto";var Wr=fI(dI),ai=t=>new Promise(e=>setTimeout(e,t)),mI=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,og=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,hI=250,wI=400,sg={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 gu(t,e){let n=Date.now()+e;for(;;){if(await Er(t))return!0;if(Date.now()>n)return!1;await ai(hI)}}async function gI(t,e){let n=sg[e]||sg.default;n.escape&&(await At(t,["Escape"],{kind:"swap-exit"}),await ai(150)),n.wipe&&await ya(t,400,{kind:"swap-exit"}),await ba(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Pe(e).submitDelayMs||0;await ai(Math.max(r,250)),await At(t,["Enter"],{kind:"swap-exit"})}async function yI(t,e){try{let{stdout:n}=await Wr(C,P("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ag(t){await Wr(C,P("respawn-pane","-k","-t",t))}async function yu(t,e={}){if(!wt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await tt(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||ln(n),a=Number(e.exitTimeoutMs)||mI;await q({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Er(t)&&(await gI(t,o),!await gu(t,a)&&(i=!0,await q({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await ag(t),!await gu(t,og))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&Wn(n),l=c?ea():null,u=await yI(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Wr(C,P("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Wr(C,P("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?pa(y,l):y}if(f&&!i&&(await ag(t),!await gu(t,og)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||pI(),m=d?n:Hc(n,p,t),h=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await on(r.cwd),s==="codex"&&await yr(r.cwd),s==="cursor"&&await br(r.cwd),await Kn(t),await Wr(C,P("set-option","-t",t,"@bostrat-sid",p)),await Wr(C,P("set-option","-t",t,"@bostrat-agent",s)),await ai(wI),await Et(t,m,{kind:"swap-launch"}),await q({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:h,forced:i,envRespawned:f}}import CI from"node:fs/promises";var bI=/^Async (?:agent|task) launched/i,kI=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 ig(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Ut(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
128
- `)){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 f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let p=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let p=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(p?Cr(p):null),detail:p?Cr(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&bI.test(kI(d.content))&&s.add(d.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 fs from"node:fs";import vI from"node:fs/promises";import or from"node:path";import SI from"node:os";import TI from"node:crypto";import AI from"node:zlib";var ii=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),ie=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,qr=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function Sn(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 bu=t=>`"${String(t).replace(/"/g,'\\"')}"`;function xI(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 cg(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:ie(`read ${Sn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${qr(r)}`:`Read ${qr(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?Sn(e.path,n):null,s=e?.glob?` --glob ${bu(e.glob)}`:"",a=ie(String(r),40);return{command:ie(`grep ${bu(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?Sn(e.target_directory,n):null;return{command:ie(`glob ${bu(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${ie(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:ie(`edit ${Sn(e.path,n)}`,140),description:`Edit ${qr(e.path)}`}:null;case"Write":return e?.path?{command:ie(`write ${Sn(e.path,n)}`,140),description:`Write ${qr(e.path)}`}:null;case"Delete":return e?.path?{command:ie(`delete ${Sn(e.path,n)}`,140),description:`Delete ${qr(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>Sn(s,n)).filter(Boolean);return{command:ie(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=xI(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:ie(`apply_patch ${Sn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${qr(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:ie(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var _I=process.env.BOSTRAT_CURSOR_CHATS||or.join(SI.homedir(),".cursor","chats");function dg(t){let e=TI.createHash("md5").update(String(t)).digest("hex");return or.join(_I,e)}function fg(t){try{let e=JSON.parse(fs.readFileSync(or.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 EI(t){let e=dg(t),n;try{n=fs.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=or.join(e,o.name),a=or.join(s,"store.db");if(!fs.existsSync(a))continue;let i=fg(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 ci(t,e){if(!t)return null;if(e){let n=or.join(dg(t),e),r=or.join(n,"store.db");if(fs.existsSync(r))return{id:e,dir:n,store:r,...fg(n)||{createdAtMs:null,updatedAtMs:null}}}return EI(t)}function lg(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function II(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=AI.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function RI(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 $I(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!ii.has(r.toolName))continue;let o=cg(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 OI=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(`
129
- `).trim():"",MI=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],ug=process.getBuiltinModule?.("node:sqlite"),Kr={turns:[],trailingCommands:[],model:null,requestIds:[]};async function ku(t,e){if(!ug)return Kr;let{DatabaseSync:n}=ug,r;try{r=new n(t,{readOnly:!0})}catch{return Kr}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 Kr;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Kr}let a=s.lastUsedModel||null,i=s.latestRootBlobId?lg(r,s.latestRootBlobId):null;if(!i)return{...Kr,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of RI(i)){let m=II(lg(r,p));if(!m)continue;let h=m.providerOptions?.cursor;if(h?.requestId&&d.push(h.requestId),h?.modelProviderMessageId&&d.push(h.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let v of m.content){let I=v?.providerOptions?.cursor?.modelName;I&&(f=I)}l.push(...$I(m.content,e).map((v,I)=>({...v,id:v.id||`${p}:${I}`}))),u.push(...MI(m.content));let y=OI(m.content);if(!y)continue;let k=[...new Set(u)];u=[],c.push({id:p,text:y,tools:k.length?k:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return Kr}finally{try{r.close()}catch{}}}function pg(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(fs.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 mg(t,e,{limit:n=80}={}){let r=ci(t,e);if(!r)return[];let{turns:o,model:s}=await ku(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=pg(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 hg(t,e){let n=ci(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await ku(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 wg(t,e,{limit:n=80}={}){let r=ci(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await ku(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=pg(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:mn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:mn(u.command,u.description),ts:i+1});return l}async function gg(t,e){let n=ci(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await vI.stat(n.store)).mtimeMs)}catch{return null}}var Gr=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}},xu=class extends Gr{transcript(e){return!e?.cwd||!e?.sessionId?null:Ne(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Xc(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?dm(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?ig(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await CI.stat(n)).mtimeMs)}catch{return null}}},vu=class extends Gr{async readMessages(e,n){return mg(e.cwd,e.sessionId,n)}async readCommands(e,n){return wg(e.cwd,e.sessionId,n)}async updatedAt(e){return gg(e.cwd,e.sessionId)}},Su=class extends Gr{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return hm(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return wm(e.cwd,this.sessionId(e),n)}async updatedAt(e){return gm(e.cwd,this.sessionId(e))}},NI=new Gr("unknown"),yg=new Map;function Tu(t){return yg.set(t.agent,t),t}function ps(t){return yg.get(t)||NI}Tu(new xu("claude",dl));Tu(new vu("cursor",ii));Tu(new Su("codex",Ko));var Au=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function _u(t,e={}){return(await ps(t.agent).readMessages(t,e)).map(Au(t))}async function bg(t,e={}){return(await ps(t.agent).readCommands(t,e)).map(Au(t))}async function kg(t,e={}){return(await ps(t.agent).readJobs(t,e)).map(Au(t))}var Sg=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Ru=/Chat about this/i,sr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,xg=/^(type something|chat about this|submit)\b/i,ar=/^[─—-]{6,}\s*$/,$u=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function ir(t){let e=t.search($u);return(e>=0?t.slice(0,e):t).trim()}function PI(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function DI(t){let e=String(t).trim();return!!(!e||ar.test(t)||$u.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function BI(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=jI(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function jI(t,e){let n=String(e).toLowerCase(),r=t.findIndex(c=>!c.answered),o=r>=0?r:t.length-1;if(!n)return o;let s=t.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(d=>d.length>3&&n.includes(d)).length;if(u)return u;let f=l.slice(0,8).trim();return f.length>=4&&n.includes(f)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function vg(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Tg=/^\s*Switch model\?\s*$/m;function LI(t,e=""){let n=String(t);if(!Tg.test(n))return null;let r=n.replace(/\r/g,"").split(`
130
- `),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 f=r[u].match(sr);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!ar.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:ir(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 Ag=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,FI=/^\s*[─—▔-]{6,}\s*$/;function UI(t,e=""){let n=String(t).replace(/\r/g,"").split(`
131
- `),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Ag.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(sr);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&&!(FI.test(n[c])||ar.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:ir(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 HI(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
132
- `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(sr);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 Eu=/Resume from summary/i,WI=/^\s*This session is\b/;function qI(t,e=""){let n=String(t);if(!Eu.test(n))return null;let r=n.replace(/\r/g,"").split(`
133
- `),o=-1;for(let u=r.length-1;u>=0;u--)if(WI.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 f=r[u].match(sr);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!ar.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>Eu.test(u.label)))return null;let i=s.map(u=>({label:ir(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 cr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function Ou(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(sr);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 _g=/Choose the text style that looks best with your terminal/,Eg=/Let's get started\./;function KI(t,e=""){let n=String(t);if(!_g.test(n)||!Eg.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
134
- `),o=Ou(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:ir(s.label),description:""}))}]}:null}var Ig=/Select login method:/;function GI(t,e=""){let n=String(t);if(!Ig.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
135
- `),o=Ou(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:ir(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 Mu=/Paste code here if prompted/,VI=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function zI(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!VI.test(o)||Mu.test(t[r]))break;n+=o}return n}function JI(t,e=""){let n=String(t);if(!Mu.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
136
- `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=zI(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 Iu=/OAuth error:/,XI=/Press Enter to retry\./;function YI(t,e=""){let n=String(t);if(!Iu.test(n)||!XI.test(n)||cr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
137
- `)].reverse().find(a=>Iu.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 Rg=/Quick safety check: Is this a project you created or one you trust/;function ZI(t,e=""){let n=String(t);if(!Rg.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
138
- `),o=Ou(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:ir(s.label),description:""}))}]}:null}var $g=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function QI(t,e=""){let n=String(t);if(cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
139
- `),o=-1;for(let l=r.length-1;l>=0;l--)if(r[l].trim()!==""){o=l;break}if(o<0)return null;let s=r[o].match($g);if(!s)return null;let a=[];for(let l=o-1;l>=0&&a.length<3;l--){let u=r[l].trim();if(!u){if(a.length)break;continue}if(ar.test(r[l])||$u.test(u))break;a.unshift(u)}let i=s[1].trim();i&&a.push(i);let c=a.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${c}`.slice(0,200),questions:[{question:c,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function tR(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Ru.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 eR(t,e=""){let n=String(t);if(!Sg.test(n)||!Ru.test(n))return null;let r=n.replace(/\r/g,"").split(`
140
- `),o=tR(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let S=i;S<c;S++){let N=r[S].match(sr);N&&(u<0&&(u=S),l.push({line:S,cursor:!!N[1],checked:N[3]!=null&&N[3]!==" ",hasCheckbox:N[3]!=null,label:N[4].trim(),desc:""}))}if(!l.length)return null;for(let S of l)for(let N=S.line+1;N<Math.min(r.length,S.line+4)&&!(sr.test(r[N])||ar.test(r[N]));N++){let D=r[N].trim();if(!(!D||DI(r[N])||xg.test(D))){S.desc=PI(D);break}}let f=l.filter(S=>!xg.test(S.label));if(!f.length)return null;let d=f.some(S=>S.hasCheckbox),p=Math.max(0,f.findIndex(S=>S.cursor)),m=s>=0?s+1:0,h=[];for(let S=m;S<u;S++){let N=r[S].trim();N&&!ar.test(r[S])&&!/[☐☒]/.test(r[S])&&h.push(N)}let y=h.join(" ")||(s>=0?vg(r[s]):"")||"The agent is asking a question",k=s>=0?BI(r[s],y):null,v=k?.currentTabLabel||(s>=0?vg(r[s]):""),I=f.map(S=>({label:ir(S.label),description:S.desc})),R=I.map(S=>S.label).join("|"),O=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${v}:${y}:${R}`.slice(0,240),M=k?`claude:ask:${k.tabs.map(S=>S.label).join("|")}`.slice(0,240):O;return{provider:"claude",box:e,multiSelect:d,cursorIndex:p,promptId:O,groupId:M,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:y,header:v,multiSelect:d,options:I}]}}function Vr(t,e){let n=String(e||"");return n?t==="claude"?Sg.test(n)&&Ru.test(n)||Tg.test(n)||Eu.test(n)||Ag.test(n.trimEnd().split(`
141
- `).pop()||"")?!0:cr.test(n)?!1:_g.test(n)&&Eg.test(n)||Ig.test(n)||Mu.test(n)||Iu.test(n)||Rg.test(n)||$g.test(n.trimEnd().split(`
142
- `).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 lr(t,e){return!t||!e?null:t.agent==="claude"?HI(e,t.name)||eR(e,t.name)||LI(e,t.name)||qI(e,t.name)||YI(e,t.name)||JI(e,t.name)||GI(e,t.name)||KI(e,t.name)||ZI(e,t.name)||QI(e,t.name)||UI(e,t.name):t.agent==="cursor"||t.agent==="codex"?Kp(t.agent,e,t.name):null}async function Cu(t){if(!t)return null;let e=await dn(t.name).catch(()=>"");return lr(t,e)}function Og(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 Mg(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 zr(t){return typeof t=="string"?t.trim():""}import{execFile as nR}from"node:child_process";import{promisify as rR}from"node:util";var Pu=rR(nR),Cg=64*1024*1024;async function yt(t,e,{input:n}={}){let r=Pu("git",["-C",t,...e],{maxBuffer:Cg});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function li(t){if(!t)return!1;try{let{stdout:e}=await yt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Ng(t){let{stdout:e}=await yt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function oR(t){try{return await yt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function ms(t,e="bostrat: subtask snapshot"){let{stdout:n}=await yt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Pu("git",["-C",t,...a],{maxBuffer:Cg,env:o,...i});try{let a=await oR(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 Pu("rm",["-f",r])}catch{}}}async function Du(t,e){let{stdout:n}=await yt(t,["status","--porcelain"]);return n.trim()?(await yt(t,["add","-A"]),await yt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function Bu(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await yt(t,["status","--porcelain"]),r=n.split(`
143
- `).filter(Boolean);return e&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function ju(t,e){if(!e)return 0;try{let{stdout:n}=await yt(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Jr(t,e,n,r){return await yt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Pg(t,e){try{return await yt(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function Dg(t,e,n){return await yt(t,["worktree","add",e,n]),e}async function ui(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await yt(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 yt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function Bg(t){await yt(t,["init","-q"]),await yt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function ne(t,e){try{await yt(t,["worktree","remove","--force",e])}catch{try{await yt(t,["worktree","prune"])}catch{}}}function sR(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 Lu(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Fu(t,e,n,r){let{stdout:o}=await yt(t,Lu(["diff","--shortstat",e,n],r));return sR(o)}async function Uu(t,e,n,r){let{stdout:o}=await yt(t,Lu(["diff","--name-only",e,n],r));return o.split(`
144
- `).map(s=>s.trim()).filter(Boolean)}async function Hu(t,e,n,r){let{stdout:o}=await yt(t,Lu(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Nu(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Nu(t,o,["--3way","--whitespace=nowarn"]),a=await aR(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Nu(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Nu(t,e,n){try{return await yt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function aR(t){try{let{stdout:e}=await yt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
145
- `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import Wu from"node:fs/promises";import Lg from"node:path";import iR from"node:os";var Fg=process.env.BOSTRAT_STATE_DIR||Lg.join(process.env.HOME||iR.tmpdir(),".bostrat"),Ug=()=>Lg.join(Fg,"usage-caps.json"),Hg=.8;function Wg(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(e.getDate()-(e.getDay()+6)%7),e.getTime()}function qg(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(1),e.getTime()}function Kg(t){let e=new Date(Wg(t));return e.setDate(e.getDate()+7),e.getTime()}function Gg(t){let e=new Date(qg(t));return e.setMonth(e.getMonth()+1),e.getTime()}var Xr={weekly:{startOf:Wg,nextStart:Kg,capKey:"weeklyUsd"},monthly:{startOf:qg,nextStart:Gg,capKey:"monthlyUsd"}},ce=null,Ku=!1,Ve={weekly:0,monthly:0},ur=new Map,Yr=new Set,qu=null,cR=t=>{let e=ur.get(t);return e||(e={weekly:0,monthly:0},ur.set(t,e)),e};function fi(t=Date.now()){for(let[e,n]of Object.entries(Xr)){let r=n.startOf(t);if(r!==Ve[e]){Ve[e]=r;for(let o of ur.values())o[e]=0;for(let o of Yr)o.includes(`:${e}:`)&&!o.endsWith(`:${r}`)&&Yr.delete(o)}}}function Vg(t,e){let n=ce?.caps?.[t]?.[Xr[e].capKey];return Number.isFinite(n)&&n>0?n:null}function Zr(t,e=Date.now()){if(!t||!Ku||!ce?.caps)return null;fi(e);let n=ur.get(t);for(let r of Object.keys(Xr)){let o=Vg(t,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:t,window:r,limitUsd:o,spentUsd:di(s),resetsAt:Xr[r].nextStart(e)}}return null}function zg(t,e){let n=`hit:${t}:${e.provider}:${e.window}:${Ve[e.window]}`;return Yr.has(n)?!1:(Yr.add(n),!0)}function jg(t,e,n,r=Date.now()){if(!t||!e)return;fi(r);let o=cR(t);for(let[s,a]of Object.entries(Xr))n>=Ve[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function lR(t,e,n=Date.now()){if(!ce?.caps||!qu)return;let r=ur.get(t);if(r)for(let[o,s]of Object.entries(Xr)){let a=Vg(t,o);if(a==null)continue;let i=r[o]||0;if(i<a*Hg||i>=a)continue;let c=`warn:${t}:${o}:${Ve[o]}`;if(!Yr.has(c)){Yr.add(c);try{let l=await qu(e);if(!l)continue;await $(l,{type:"usage.cap.warning",actor:"system",provider:t,payload:{provider:t,window:o,limitUsd:a,spentUsd:di(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function Jg(t){let e={};for(let[n,r]of Object.entries(t||{})){if(typeof n!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n))throw new Error(`bad provider key "${n}"`);let o={};for(let s of["weeklyUsd","monthlyUsd"]){let a=r?.[s];if(a==null||a==="")continue;let i=Number(a);if(!Number.isFinite(i)||i<=0)throw new Error(`${n}.${s} must be a positive number or null`);o[s]=i}Object.keys(o).length&&(e[n]=o)}return e}async function pi(t,{source:e="local",updatedAt:n=Date.now()}={}){ce={caps:Jg(t),updatedAt:n,source:e};try{await Wu.mkdir(Fg,{recursive:!0}),await Wu.writeFile(Ug(),JSON.stringify(ce,null,2))}catch{}return hs()}function hs(t=Date.now()){fi(t);let e=new Set([...ur.keys(),...Object.keys(ce?.caps||{})]),n={};for(let r of e){let o=ur.get(r);n[r]={weeklyUsd:di(o?.weekly||0),monthlyUsd:di(o?.monthly||0)}}return{caps:ce?.caps||{},configured:!!ce,seeded:Ku,updatedAt:ce?.updatedAt??null,spent:n,windows:{weekly:{start:Ve.weekly,resetsAt:Kg(t)},monthly:{start:Ve.monthly,resetsAt:Gg(t)}},warnPct:Hg}}async function Xg({listTracks:t,getTrack:e}={}){qu=e||null,fi();try{let o=JSON.parse(await Wu.readFile(Ug(),"utf8"));ce={caps:Jg(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{ce=null}let n=Date.now();Ht.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=De(o);jg(o.provider||"unknown",s,o.ts??Date.now()),lR(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(Ve.weekly,Ve.monthly);for(let o of await t?.()||[]){let s;try{s=await B(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||jg(a.provider||"unknown",De(a).usd,i,n)}}return Ku=!0,hs()}var di=t=>Math.round(t*1e4)/1e4;import uR from"node:fs/promises";import dR from"node:os";import Yg from"node:path";var mi=t=>new Promise(e=>setTimeout(e,t)),fR=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,pR=300,ey=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 5"},{alias:"sonnet",label:"Sonnet 5"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],mR=new Set([...ey.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),hR=[{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.6-terra",label:"GPT-5.6 Terra"},{alias:"gpt-5.6-luna",label:"GPT-5.6 Luna"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}],ny=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),ry=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{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.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}];function oy(t){return t==="codex"?hR:t==="cursor"?ry:ey}function Zg(t){let e=String(t||"").split(`
146
- `),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 Qg(t){let e=String(t||"").split(`
147
- `),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 wR(t){let e=String(t||"").split(`
148
- `);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 ty(t){let e=String(t||"").split(`
149
- `),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 sy(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&&!ny.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=ry.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!mR.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ws(t,e,{timeoutMs:n=fR}={}){let r=Date.now()+n;for(;;){let o=await mt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await mi(pR)}}async function gR(t,e){return await Et(t,`/model ${e.model}`,Pe("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function yR(t,e){await ga(t),await Et(t,"/model",{enter:!1}),await mi(250),await At(t,["Enter"],{kind:"model-switch"});let n=await ws(t,Zg);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await At(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=wR(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await Et(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await ws(t,i=>Qg(i)||!Zg(i)&&o(i)&&{done:o(i)});if(!s)throw await At(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?ny.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 At(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await Et(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await ws(t,i=>!Qg(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 bR(){let t=Yg.join(process.env.CURSOR_CONFIG_DIR||Yg.join(dR.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await uR.readFile(t,"utf8"))?.model||null}catch{return null}}async function kR(t,e){await ga(t),await ba(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await ws(t,ty),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 ws(t,ty,{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 At(t,["Down"],{kind:"model-switch"}),await mi(250)}if(!n||!s||!o(s)){await At(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await At(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 At(t,["Enter"],{kind:"model-switch"}),await mi(800);let a=await bR();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 ay(t,e,n){return t==="codex"?yR(e,n):t==="cursor"?kR(e,n):gR(e,n)}var Xt=t=>t||null;function wi(t){let e=t||{},n=xR(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function xR(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Xt(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:Xt(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:Xt(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:Xt(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,checkpoint:t.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Xt(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:Xt(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:Xt(t.agent),payload:{...Qr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Xt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Qr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{change:"subtask-resumed",...Qr(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",...Qr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Qr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Qr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var hi=Promise.resolve(),Vu=t=>(hi=hi.then(t).catch(()=>{}),hi),cy=()=>hi;function ly(t,e){let n=wi(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Vu(()=>$(r,n))}var iy=new Set;async function uy(t){if(!(!t?.id||iy.has(t.id))){iy.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=wi(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(t,o)}}catch{}}}function Qr(t){let{type:e,at:n,...r}=t;return r}var Gu=new Map;function dy(t,e,n){!n?.promptId||Gu.get(t.id)===n.promptId||(Gu.set(t.id,n.promptId),Vu(()=>$(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Xt(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 zu(t,e,n,r={}){Gu.delete(t.id),Vu(()=>$(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Xt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as c$}from"node:crypto";var py=/(?<!\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,my=/(?<!\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,hy=/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,wy=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|clean|interactive)\s)rebase(?!s\b)(?:\s+(?:it|this|that|them|(?:this|that|the)\s+branch))?(?:\s+(?:on(?:to)?|against|atop|over)\s+\S+)?/.source,vR=new RegExp(`\\b(?:${py}|${my}|${hy}|${wy})\\b`,"i"),SR=/\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,TR=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,AR=90;function _R(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function gy(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of _R(n).reverse()){if(!vR.test(r))continue;let o=r.endsWith("?")&&r.length<=AR&&TR.test(r);if(SR.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var yy=["commit","rebase","push","pr"],ER=new RegExp(`\\b(?:${py})\\b`,"i"),IR=new RegExp(`\\b(?:${wy})\\b`,"i"),RR=new RegExp(`\\b(?:${my})\\b`,"i"),$R=new RegExp(`\\b(?:${hy})\\b`,"i"),OR=/\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 by(t){let e=String(t||""),n=[];ER.test(e)&&n.push("commit"),IR.test(e)&&n.push("rebase"),RR.test(e)&&n.push("push");let r=OR.test(e);!r&&$R.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 gi(t,{branch:e=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(t),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=e?` on this branch (${e})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${e?` (${e})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${e?` (${e})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}var MR=3e4;function ky(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<=MR&&n.push({box:r.box,from:r.from,to:e+1e12}),n}var CR=2500;function xy(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+CR||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}var fy={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function vy(t){let e=(Array.isArray(t)?t:[]).filter(r=>fy[r]).map(r=>fy[r]);return`The ship you were given the go-ahead for${e.length?` (${e.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}import{execFile as jR}from"node:child_process";import{promisify as LR}from"node:util";import NR from"node:fs";import Ju from"node:fs/promises";import _y from"node:path";import PR from"node:os";var Ey=process.env.BOSTRAT_STATE_DIR||_y.join(process.env.HOME||PR.tmpdir(),".bostrat"),yi=_y.join(Ey,"github-metrics.json"),Iy=60*6e4,Sy=5*6e4,DR=6e4,to=Date.now(),ze=new Map,Tn=null,dr=0,Ty=0,bi=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(NR.readFileSync(yi,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Iy?(to=t.windowStart,ze=new Map(Object.entries(t.byCaller||{})),Tn=t.lastHour||null):bi(new Map(Object.entries(t.byCaller||{})))&&(Tn={start:t.windowStart,total:bi(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var Ay=Promise.resolve();function Ry(t,e=!1){if(!e&&t-Ty<DR)return;Ty=t;let n=JSON.stringify({windowStart:to,byCaller:Object.fromEntries(ze),lastHour:Tn});Ay=Ay.then(()=>Ju.mkdir(Ey,{recursive:!0})).then(()=>Ju.writeFile(`${yi}.tmp`,n)).then(()=>Ju.rename(`${yi}.tmp`,yi)).catch(()=>{})}function $y(t){t-to<Iy||(Tn={start:to,total:bi(ze),byCaller:Object.fromEntries(ze)},Tn.total&&console.log(`[github] ${Tn.total} calls last hour`,Tn.byCaller),to=t,ze=new Map,Ry(t,!0))}function Je(t="other",e=Date.now()){$y(e),ze.set(t,(ze.get(t)||0)+1),Ry(e)}function Oy(t=Date.now()){return $y(t),{windowStart:to,total:bi(ze),byCaller:Object.fromEntries(ze),lastHour:Tn,backoffUntil:dr>t?dr:null}}var BR=/rate limit|too quickly|abuse detection|HTTP 429/i;function An(t){return BR.test(String(t?.message||t||""))}function _n(t,e=Date.now()){return dr<=e&&console.warn(`[github] rate limited \u2014 backing off ${Sy/6e4} min:`,String(t?.message||t||"").slice(0,200)),dr=Math.max(dr,e+Sy),dr}function ki(t=Date.now()){return Math.max(0,dr-t)}var My=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,gs=new Map;function xi(t,e,n){let{state:r,reason:o}=el(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of gs)a-c.at>=My()&&gs.delete(i);gs.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=gs.get(t);return s?Date.now()-s.at>=My()?(gs.delete(t),!1):s.busy:!1}async function Xu(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await mt(t,{lines:_t}).catch(()=>"");if(!xi(t,e,r))return Jp(e,r)?{working:!0,prompted:!1}:n;if(Vr(e,r)){let o=await dn(t).catch(()=>r);if(lr({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function vi(t,e){return(await Xu(t,e)).working}var Cy=LR(jR),Ny=[4e3,15e3,45e3],FR=2500,UR=5*6e4;async function Yu(t,e,{midTurn:n=vi,pollMs:r=FR,capMs:o=UR}={}){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 HR=15e3,WR=9e4,qR=2e3,KR=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,GR=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,VR=/\s--web\b/,Py=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,zR=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,JR=/^track\/.+-([0-9a-f]{8})$/;function XR(t,e){let n=String(t||"").match(JR);return!!n&&n[1]!==e}function Zu(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Dy(t){let e=String(t||""),n=e.match(KR);if(!n)return null;let r=Zu(e,n.index);return VR.test(r)?null:{repo:r.match(Py)?.[1]??null,head:r.match(zR)?.[1]??null}}var YR=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function By(t){let e=String(t||""),n=e.match(GR);if(!n)return null;let r=Zu(e,n.index),o=Zu(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("-")){YR.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Py)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var ZR="number,url,title,headRefName,createdAt";async function QR(t,e,{exclude:n=()=>!1}={}){let r=async i=>{Je("observer");let{stdout:c}=await Cy("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",ZR],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw An(l)&&_n(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=e-HR&&!n(c)),s=await me({slug:t}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function t$(t,e){if(e)return[e];let n=[];for(let r of jy(t)){let o=await et(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Oe().catch(()=>[])){let o=await et(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function jy(t){let e=t.workspace;return e?.kind==="multi"?(e.repos??[]).map(n=>n?.base).filter(Boolean):[e?.base||t.cwd].filter(Boolean)}async function e$(t,e,n){let r=t.workspace?.branch,o=e?.head??null,s=c=>r&&c===r||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||XR(c.headRefName,t.id),i=[];for(let c of await t$(t,e?.repo??null)){let l;try{l=await QR(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await Bt(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===t.id&&f.announced)return{settled:!0};if(f&&f.trackId!==t.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==t.id||f.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}var En=new Set;function Ly(t,e,{delays:n=Ny,turnEnd:r}={}){let o=Dy(e.command);if(!o)return null;let s=!!By(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(En.has(a))return null;En.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>setTimeout(h,l));let u=await e$(t,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await Jt({repo:f,number:d.number,trackId:t.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await cs(f,d.number))?null:(await Yu(e.box,e.agent,r),await $({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:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{En.delete(a)}})();return c.catch(()=>En.delete(a)),c}var n$="number,url,title,headRefName,state";function Fy(t,e,{delays:n=Ny,turnEnd:r,openedWait:o}={}){let s=By(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(En.has(a))return null;En.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let m=jy(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of m){let y=await et(h).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=t.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,h)=>{let y=await Bt(c,m.number);return y&&y.trackId!==t.id?null:(y||await Jt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await ls(c,m.number),h&&((await B(t).catch(()=>[])).some(I=>I.type==="pr.state.changed"&&(I.payload?.change==="auto-armed"||I.payload?.change==="merged")&&I.payload?.number===m.number)||(await Yu(e.box,e.agent,r),await $({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:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(O=>setTimeout(O,m));let h=[];for(let O of f)try{Je("observer");let{stdout:M}=await Cy("gh",["pr","view",String(l),"--repo",O,"--json",n$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await me({slug:O})}}),S=JSON.parse(M||"null");S?.number&&h.push({slug:O,pr:S})}catch(M){An(M)&&_n(M)}let y=h.find(O=>O.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!y)continue;c=y.slug;let k=y.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return d(k,!0);continue}let v=await Bt(c,k.number);if(v&&v.trackId!==t.id)return null;v||await Jt({repo:c,number:k.number,trackId:t.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Yu(e.box,e.agent,r);let I=await B(t).catch(()=>[]);if(I.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="merged"&&O.payload?.number===k.number))return null;let R=O=>O.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="opened"&&M.payload?.number===k.number),z=0;if(!R(I)){if(Dy(e.command)&&await cs(c,k.number)){let O=Date.now();z=O+1,await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:O,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await Bt(c,k.number))?.announced){let O=o?.capMs??WR,M=o?.pollMs??qR,S=Date.now()+O;for(;Date.now()<S&&(await new Promise(N=>setTimeout(N,M)),!R(await B(t).catch(()=>[]))););}}return await $({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(),z),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await Rn(c,k.number).catch(()=>{}),await In(c,k.number).catch(()=>{}),await vn(c,k.number,"merged"),k}return p?d(p,!1):null}finally{En.delete(a)}})();return i.catch(()=>En.delete(a)),i}import r$ from"node:fs/promises";import td from"node:path";import o$ from"node:os";var s$=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/;function Qu(t,e){let n=new RegExp(`\\b${e}="([^"]*)"`).exec(t);return n?n[1]:null}function Si(t){let e=s$.exec(String(t||""));if(!e)return null;let[,n,r]=e,o=Qu(n,"from"),s=o?o.replace(/^uds:/,""):null,a=Uy(s);return{socket:s,pid:a,name:Qu(n,"from-name"),mode:Qu(n,"from-mode"),text:r.trim()}}function Uy(t){let e=/(\d+)\.sock$/.exec(String(t||""));return e?Number(e[1]):null}function a$(){return process.env.CLAUDE_CONFIG_DIR?td.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):td.join(o$.homedir(),".claude","sessions")}async function i$(t){let e=Uy(t);if(!e)return null;try{let n=await r$.readFile(td.join(a$(),`${e}.json`),"utf8"),r=JSON.parse(n);return{pid:e,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function Hy(t,e){let n=await i$(t?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof e=="function")try{o=await e(n?.sessionId??null,r)??null}catch{o=null}return{socket:t?.socket??null,pid:n?.pid??t?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??t?.name??null,mode:t?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}function ys(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Wy(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function qy(t){return`job|${t.box}|id:${t.id}`}var ed=new Map,Ti=new Map;async function nd(t){return ed.has(t.id)?ed.get(t.id):(Ti.has(t.id)||Ti.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(ys({role:o,sourceId:r.payload.sourceId})),n.keys.add(ys({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(ys({role:"user",sourceId:r.payload.sourceId})),n.keys.add(ys({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(Wy({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(qy({id:r.payload.id,box:r.boxId}));return ed.set(t.id,n),n}).finally(()=>Ti.delete(t.id))),Ti.get(t.id))}async function Ky(t,e){try{let n=(await nd(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=ys(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?Si(r.text):null;if(s){await $(t,{type:"peer.message",actor:"peer",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:s.text,ts:r.ts,keyText:String(r.text||"").slice(0,40),from:await Hy(s,Ai),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}if(await $(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 a=gy(r.text);if(a){let{offered:i,toPr:c}=by(a),l;if(t.workspace?.kind==="multi"){let u=await bs(t).catch(()=>[]);l=u.length?u.map(f=>({name:f.name,slug:f.repo??null})):void 0}await $(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:c$(),ts:r.ts,excerpt:a,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:i||void 0,toPr:c||void 0,repos:l}})}}}}catch{}}async function Gy(t,e){try{let n=await nd(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=Wy(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await $(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}}),Ly(t,o),Fy(t,o))}}catch{}}async function Vy(t,e){try{let n=await nd(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=qy(o);n.keys.has(s)||(n.keys.add(s),await $(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{}}function l$(t){let e=[],n=null;for(let r of String(t||"").split(`
150
- `)){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 rd=new Map;async function u$(t,e){let n=`${t.id}:${e.name}`;if(!rd.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);rd.set(n,o)}return rd.get(n)}async function zy(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Vn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await u$(t,e),o=l$(await Ut(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 $(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:gn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await $(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 _i from"node:fs";import sd from"node:path";import d$ from"node:os";var f$=process.env.BOSTRAT_STATE_DIR||sd.join(process.env.HOME||d$.tmpdir(),".bostrat"),Xy=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,p$="https://cursor.com/api/dashboard/get-filtered-usage-events",Jy=100;async function m$({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(p$,{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:Jy})});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<Jy)break}return r}function h$(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 w$=5*6e4,$n={at:0,rows:null,error:null},Ei=()=>sd.join(f$,"cursor-usage.json");function Yy(){try{return JSON.parse(_i.readFileSync(Ei(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function g$(t){try{_i.mkdirSync(sd.dirname(Ei()),{recursive:!0});let e=Ei()+".tmp";_i.writeFileSync(e,JSON.stringify(t)),_i.renameSync(e,Ei())}catch{}}var y$=5e3;async function b$({fetchImpl:t,now:e=Date.now()}={}){if($n.rows&&e-$n.at<w$)return $n.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Yy(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await m$({sinceMs:o,cookie:n,fetchImpl:t});$n={at:e,rows:s.map(h$).filter(Boolean),error:null}}catch(s){$n={at:e,rows:null,error:String(s?.message||s)}}return $n.rows}var ad=()=>({enabled:Xy(),lastPollAt:$n.at||null,error:$n.error}),od=new Map;async function k$(t,e){let n=`${t.id}:${e.name}`;if(!od.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);od.set(n,o)}return od.get(n)}async function Zy(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Xy())return;let r=await b$({fetchImpl:n});if(!r||!r.length)return;let o=await hg(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await k$(t,e),a=Yy(),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",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=gn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await $(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...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}})}g$({sinceMs:c,seen:[...i].slice(-y$)})}catch{}}import{execFile as A$}from"node:child_process";import{promisify as _$}from"node:util";import Qy from"node:fs/promises";import tb from"node:path";import x$ from"node:os";var eb=process.env.BOSTRAT_STATE_DIR||tb.join(process.env.HOME||x$.tmpdir(),".bostrat"),v$=tb.join(eb,"github-latency.ndjson"),S$=100,Ii=[],id=new Map;function Ri({change:t,repo:e=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:t,repo:e,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};Ii.push(l),Ii.length>S$&&Ii.shift();let u=id.get(t)||{count:0,measured:0,totalMs:0,maxMs:0};return u.count+=1,c!=null&&(u.measured+=1,u.totalMs+=c,u.maxMs=Math.max(u.maxMs,c)),id.set(t,u),T$(l),l}async function T$(t){try{await Qy.mkdir(eb,{recursive:!0}),await Qy.appendFile(v$,JSON.stringify(t)+`
151
- `)}catch{}}function nb(){let t={};for(let[e,n]of id)t[e]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:t,recent:Ii.slice(-20)}}async function E$(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([no("git",["-C",t,"rev-parse","HEAD"]),no("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await $({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var no=_$(A$),I$={timeout:3e4,maxBuffer:8*1024*1024};async function re(t,e,n={}){let{caller:r="prs",...o}=n,s=await me({cwd:t});Je(r);try{let{stdout:a}=await no("gh",e,{cwd:t,...I$,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw An(a)&&_n(a),a}}var $i=null;async function ud(){if($i&&Date.now()-$i.at<6e4)return $i.data;let t;try{Je("auth");let e=await no("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"||e?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await Ro();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return $i={at:Date.now(),data:t},t}async function R$(t){try{let{stdout:e}=await no("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function ob(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r=t.completedAt||t.createdAt||null,o;return t.__typename==="StatusContext"?o=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?o="pass":o="fail",{name:e,state:o,url:n,completedAt:r}}function cd(t){let e=(t||[]).map(ob),n=e.filter(s=>s.state==="pending").length,r=e.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=e.length&&!n&&r.length?Math.max(...r):null;return{total:e.length,passed:e.filter(s=>s.state==="pass").length,failed:e.filter(s=>s.state==="fail").length,pending:n,failing:e.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}var dd="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",sb=dd.replace("statusCheckRollup,","");function fd(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function ab(t,e){if(!e)return null;let n=await et(t);if(!n)return null;try{let r=await re(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 ib(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,completedAt:e.status==="completed"&&e.updated_at||null}))}async function cb(t,e){let n=await ab(t,e);return n===null?{...cd([]),unavailable:!0}:cd(ib(n))}function Oi(t,e=cd(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 $$=9e4,eo=new Map;function O$(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){eo.delete(s);continue}r.add(s),eo.has(s)||eo.set(s,n),o.mergeabilityPending=n-eo.get(s)<$$}for(let o of eo.keys())o.startsWith(`${t}#`)&&!r.has(o)&&eo.delete(o);return e}async function ro(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await re(n.path,["pr","view",String(r),"--json",dd]);return Oi(JSON.parse(o))}catch(o){if(!fd(o))throw o;let s=await re(n.path,["pr","view",String(r),"--json",sb]),a=JSON.parse(s);return Oi(a,await cb(n.path,a.headRefOid))}}var rb=(t,e)=>t.number-e.number;async function xs(t,{caller:e="prs"}={}){try{let n=await re(t,["pr","list","--json",dd,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>Oi(r)).sort(rb)}catch(n){if(!fd(n))throw n;let r=await re(t,["pr","list","--json",sb,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Oi(o,await cb(t,o.headRefOid))))).sort(rb)}}var M$=3e4,C$=1500,ks=new Map;function Re(){ks.clear()}async function N$(t,e){if(!t)return e;for(let n of e){let r=await Bt(t,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await ei(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await ri(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function lb(){let t=await ud();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Oe())await R$(a.path)&&e.push(a);let n=Date.now(),r=ki(n)>0,o=await Promise.all(e.map(async a=>{let i=ks.get(a.path),c=i?.pending?C$:M$;if(i&&(r||n-i.at<c))return i.entry;try{let l=await et(a.path).catch(()=>null),u=O$(a.path,await xs(a.path,{caller:"tab"}),n);if(await N$(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(h=>h.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Ri({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return ks.set(a.path,{at:Date.now(),entry:f,pending:d}),f}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Ie(l)}}})),s=new Set(e.map(a=>a.path));for(let a of ks.keys())s.has(a)||ks.delete(a);return{auth:t,repos:o}}async function Xe(t){let e=(await Oe()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function fr(t){return Eo(t)}var vs=new Set(["squash","merge","rebase"]);async function oo(t,e,{method:n="squash"}={}){let r=await Xe(t);if(!vs.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 me({cwd:r.path})).GH_TOKEN){let a=await Sc().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await re(r.path,s)}catch(a){throw new Error(Ie(a))}finally{Re()}return{ok:!0,number:o}}async function Ss(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await re(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Ie(s))}finally{Re()}let o=await et(n.path).catch(()=>null);return o&&await Gw(o,r).catch(()=>{}),{ok:!0,number:r}}async function pd(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await re(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Ie(o))}finally{Re()}return{ok:!0,number:r}}var P$=2500,D$=9e4,B$=t=>new Promise(e=>setTimeout(e,t));function md(t){return t.green&&!t.isDraft}function j$(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})`}function ub(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:md(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function db(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var ld=new Map;function fb(){let t={};for(let[e,n]of ld)t[e]={...n};return t}async function pb(t,{method:e="merge",onMerged:n=null}={}){let r=await Xe(t);if(!vs.has(e))throw new Error(`invalid merge method: ${e}`);if(ld.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await xs(r.path)).filter(md).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 ld.set(r.path,s),F$(s,o,{listPrs:()=>xs(r.path),merge:a=>oo(r.path,a,{method:e}),sleep:B$,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function L$(t,e){let n=Date.now()+(e.settleMaxMs??D$),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(P$)}}async function F$(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 L$(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(!md(a)){t.skipped.push({number:a.number,title:a.title,reason:j$(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(),Re()}}async function hd(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await no("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Ie(l)}`)}await E$(t);let o=n||"";if((await me({cwd:t})).GH_TOKEN){let l=await Sc().catch(()=>null);l&&(o=`${o?`${o}
152
-
153
- `:""}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 re(t,s)}catch(l){throw new Error(Ie(l))}finally{Re()}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 mb(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await re(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(!fd(i))throw i;let c=await re(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await ab(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Ie(i));o=ib(l).map(ob).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=U$(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await re(n.path,["run","view",c,"--log-failed"]).then(u=>H$(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function U$(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function H$(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
122
+ }`,{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 pI(t,e){let n=await Ie(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 mI(t,e,n){if(!(await Ie(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 Wr(t){return((await Ie(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var Rw=255;async function su(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>Rw,a=s?`${o.slice(0,Rw-1).trimEnd()}\u2026`:o,i=await Ie(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function au(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Ie(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 Dw={linear:{api:iu,config:tr},jira:{api:ru,config:Za}};async function mu(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(Dw)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function rr(t){let e=Dw[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function hu(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 Bw=()=>process.env.BOSTRAT_STATE_DIR||Nw.join(process.env.HOME||hI.tmpdir(),".bostrat"),du=()=>Nw.join(Bw(),"tickets.json"),jw=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),ti=t=>`${jw()}/track/${t}`,kn=null,cu=null,lu=Promise.resolve();async function ds(){return kn||(cu??=ls.readFile(du(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(kn=t,!e&&mt()){let{records:n,source:r}=await It("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;kn[s]=i}Object.keys(kn).length&&await fu().catch(()=>{})}}return kn}).finally(()=>cu=null),cu)}async function fu(){let t=JSON.stringify({tickets:kn},null,2),e=`${du()}.${process.pid}.tmp`;await ls.mkdir(Bw(),{recursive:!0}),await ls.writeFile(e,t),await ls.rename(e,du())}async function xn(t=null){if(lu=lu.then(fu,fu),await lu.catch(()=>{}),!t)return;let e=kn?.[t];e?Bt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):se("tickets",t).catch(()=>{})}async function Re(t){return(await ds())[t]??null}async function ei(){return{...await ds()}}async function Lw(){return(await mu()).length>0}async function qr(t,e,{source:n="api"}={}){let r=await mu();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 f=u.api.parseIssueRef(e);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}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,...t.workspace?.kind==="multi"?{repos:(t.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await ds())[t.id]=c,await xn(t.id),await I({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 us(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await cs(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Hw(t.id,c,"linked"),c}async function wu(t){let e=await ds();return e[t]?(delete e[t],await xn(t),!0):!1}var pu=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Fw(t,e){if(!t.track?.cwd)return null;let n=ke(await P({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function wI(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${pu(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${pu(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function gI(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.track.repos?.length&&n.push(t.track.repos.join(" + ")),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(pu(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function us(t,e){let n=await rr(e);if(!n)return;let r=await Fw(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:ti(t),title:`Bostrat Track: ${e.track.name}`,subtitle:gI(e,r)})}function ni(t){let e=new URL(jw());return`${e.protocol}//${t}.${e.host}/`}var yI={linked:"linked",opened:"prOpened",merged:"prMerged"};function bI(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 Uw(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await xn(t),await I({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 Hw(t,e,n){let r=await rr(e);if(!r)return;let o=r.cfg.ticketFlow?.[yI[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=hu(s,a),c=bI(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 Uw(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await I({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Pw(),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 Ww(t){let e=await Re(t);if(!e)throw new Error("no ticket linked to this Track");let n=await rr(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=hu(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await xn(t)),{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 gu(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await Re(t);if(!r)throw new Error("no ticket linked to this Track");let o=await rr(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=hu(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 xn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Uw(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function qw(t,e){let n=await Re(t);if(!n)throw new Error("no ticket linked to this Track");let r=await rr(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 I({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 Kw(t,e,n){if(!e)throw new Error("offerId required");let r=await P({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await gu(t.id,o.payload.to?.id,{mode:"offer"})),await I({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 $w=50;async function Gw(){let t=await tr();if(!t)throw new Error("no ticketing integration configured");return Wr(t)}function kI(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>$w)throw new Error(`too many issues (${n.length}); cap is ${$w} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function xI(t,e,n,r){let o=r??await Wr(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 Re(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 Vw(t,e){let n=await tr();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=kI(e),o=await Wr(n),s=await xI(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Pw();return await I({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 zw(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await P({id:t.id,cwd:t.cwd}),s=o.find(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 I({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 tr();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Wr(a)).find(h=>h.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=[],f=null;if(i.epic){let m=await su(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let h=await au(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:m.title,message:h?.message||String(h)})}await I({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 I({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 d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await Re(t.id)&&(p=await qr(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function cs(t,e){let n=await rr(t);n&&await n.api.createComment(n.cfg,t,e)}async function SI(t,e,n){let r=await rr(t);if(!r)return null;let o=await Le(e);if(!o)return null;let s=ce(o,n);if(!s)return null;let a=await ls.readFile(ne(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 Ow=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),uu=new Map;function vI(t,e){if(Ow()<=0)return us(t,e);if(uu.has(t))return;let n=setTimeout(()=>{uu.delete(t),Re(t).then(r=>r?us(t,r):null).catch(()=>{})},Ow());n.unref?.(),uu.set(t,n)}var Cw=new Map;async function Mw(t,e,n){let r=Cw.get(t)??0;Date.now()-r<600*1e3||(Cw.set(t,Date.now()),await I({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function TI(t,{resolveTrack:e=null}={}){let n=await Re(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await mu())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 qr(s,o,{source:"chat"})}catch(a){await Mw(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 xn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await xn(t.trackId),await us(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 xn(t.trackId),await us(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Fw(n,t.trackId).catch(()=>null):null;await cs(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
123
+
124
+ Agent cost for this Track: ${wI(o)}.`:"")),await Hw(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await SI(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await cs(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
125
+
126
+ ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await cs(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 cs(n,`**Bostrat** \u2014 live preview **${r.name}**: ${ni(r.slug)}
127
+ _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await vI(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Mw(t.trackId,s?.cwd??null,o)}}function Jw({resolveTrack:t}){qt.on("event",e=>{TI(e,{resolveTrack:t}).catch(()=>{})})}async function Yw(){if(!mt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await It("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await ds(),r=0;for(let[o,s]of Object.entries(n))await Bt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import AI from"node:crypto";import ri from"node:fs/promises";import Xw from"node:path";import _I from"node:os";var Zw=process.env.BOSTRAT_STATE_DIR||Xw.join(process.env.HOME||_I.tmpdir(),".bostrat"),ku=Xw.join(Zw,"pr-links.json"),G=(t,e)=>`${t}#${Number(e)}`,Qw=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=AI.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},Sn=null,yu=null,bu=Promise.resolve();async function vt(){return Sn||(yu??=ri.readFile(ku,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(Sn=t,!e&&mt()){let{records:n,source:r}=await It("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;Sn[G(i.repo,i.number)]=i}Object.keys(Sn).length&&await xu().catch(()=>{})}}return Sn}).finally(()=>yu=null),yu)}async function xu(){let t=JSON.stringify({links:Sn},null,2),e=`${ku}.${process.pid}.tmp`;await ri.mkdir(Zw,{recursive:!0}),await ri.writeFile(e,t),await ri.rename(e,ku)}async function Ot(t){bu=bu.then(xu,xu),await bu.catch(()=>{});let e=Sn?.[t];e&&Bt("prlinks",{id:Qw(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var EI=new Set(["api","observed","reconciled"]);async function Xt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await vt(),c=G(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:EI.has(a),firstSeen:Date.now()},await Ot(G(t,e)),{link:i[c],created:!0})}async function fs(t,e){if(!t||e==null)return null;let n=(await vt())[G(t,e)];return!n||n.announced?null:(n.announced=!0,await Ot(G(t,e)),n)}async function Lt(t,e){return!t||e==null?null:(await vt())[G(t,e)]??null}async function vn(t,e,n){if(!t||e==null)return null;let r=(await vt())[G(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await Ot(G(t,e)),r):null}async function tg(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await vt())[G(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Ot(G(t,e)),o):null}async function eg(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await vt())[G(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Ot(G(t,e)),o):null}async function oi(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await vt())[G(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Ot(G(t,e)),r)}async function si(t,e){if(!t||e==null)return null;let n=(await vt())[G(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Ot(G(t,e)),n)}async function ai(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await vt())[G(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Ot(G(t,e)),r)}async function ii(t,e){if(!t||e==null)return null;let n=(await vt())[G(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Ot(G(t,e)),n)}async function ng(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await vt())[G(t,e)];return r?(r.baseFoldAt=n,await Ot(G(t,e)),r):null}async function rg(t,e){if(!t||e==null)return null;let n=(await vt())[G(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Ot(G(t,e)),n)}async function og(t,e,n){if(!t||e==null||!n)return null;let r=(await vt())[G(t,e)];return r?(r.lastHeadSha=n,await Ot(G(t,e)),r):null}async function ps(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await vt())[G(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Ot(G(t,e)),r):null}async function or(t,e,n=!0){if(!t||e==null)return null;let r=(await vt())[G(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Ot(G(t,e)),r):null}async function ms(t,e,n=!0){if(!t||e==null)return null;let r=(await vt())[G(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Ot(G(t,e)),r):null}async function sg(t,e,n){if(!t||e==null)return null;let r=(await vt())[G(t,e)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Ot(G(t,e)),r}async function hs(t,e){if(!t||e==null)return null;let n=(await vt())[G(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Ot(G(t,e)),n)}var II=new Set(["merged","closed","lost"]);async function ag(){return Object.values(await vt()).filter(t=>!II.has(t.state))}async function $e(){return Object.values(await vt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function ig(){if(!mt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await It("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await vt(),r=0;for(let[o,s]of Object.entries(n))await Bt("prlinks",{id:Qw(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var lg=t=>Math.round(t*100)/100;function RI(t){return!t||t.kind==="setup"?[]:Gt(t.workspace).filter(e=>e&&(e.repo||e.base)).map(e=>({key:e.repo||e.base,slug:e.repo??null,base:e.base??null,name:e.repo?e.repo.split("/").pop():cg.basename(e.base)}))}function ug(t){let e=new Map;for(let n of t||[])for(let r of RI(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function ci(t){let e=t.events?.length?t.events[t.events.length-1].at:0;return Math.max(t.createdAt||0,t.archivedAt||0,t.lastResponseAt||0,e||0)||null}var Su=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function $I(t){if(!Number.isFinite(t)||t<=0)return null;let e=t/864e5;if(e>=1)return`${Math.round(e)}d`;let n=t/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(t/6e4))}m`}function vu(t,e=[]){let n=[],r=t?.events||[],o=new Map,s=0;for(let u of e){if(u.type!=="pr.state.changed")continue;let f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,p]=f.split("#");return`${d.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Su(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Su(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${Su(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=ke(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?$I(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function OI(t){let e=[],n=[...t?.boxes||[]].sort((o,s)=>(o.createdAt||0)-(s.createdAt||0));for(let o of n){if(o.role!=="lead"&&o.role!=="handed-off")continue;let s=Array.isArray(o.epochs)&&o.epochs.length?o.epochs.map(a=>a.agent):[o.agent];for(let a of s)a&&e[e.length-1]!==a&&e.push(a)}let r=new Set(n.filter(o=>o.role==="subtask"&&o.agent&&!e.includes(o.agent)).map(o=>o.agent));return{lead:e,subtask:[...r]}}function dg(t,e,n){let r=new Set(t.tracks.map(d=>d.id)),o=0,s=0,a=0,i=null,c=null,l=0;for(let d of t.tracks){d.archivedAt?s++:o++,d.beat?.text&&a++;let p=ci(d);p&&(!i||p>i)&&(i=p),d.createdAt&&(!c||d.createdAt<c)&&(c=d.createdAt),l+=e.get(d.id)||0}let u=0,f=0;for(let d of n)r.has(d.trackId)&&(t.slug&&d.repo!==t.slug||t.slug&&(d.state==="merged"?f++:d.state!=="closed"&&u++));return{key:t.key,slug:t.slug,base:t.base,name:t.name,tracks:{total:t.tracks.length,live:o,archived:s,withBeat:a},prs:{open:u,merged:f},usd:lg(l),firstActivityAt:c,lastActivityAt:i}}async function fg(t){let e=ug(t),n=await $e().catch(()=>[]),r=new Map,o=new Set;for(let a of e.values())for(let i of a.tracks){if(o.has(i.id))continue;o.add(i.id);let c=await P(i).catch(()=>[]);r.set(i.id,ke(c).totalUsd)}let s=[...e.values()].map(a=>dg(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function pg(t,e){let r=ug(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([$e().catch(()=>[]),Xn().catch(()=>[]),os().catch(()=>[]),Zl().catch(()=>[]),ei().catch(()=>({}))]),l=new Map,u=[],f=(d,p)=>(ci(p)||p.createdAt||0)-(ci(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let p=await P(d).catch(()=>[]),m=ke(p);l.set(d.id,m.totalUsd);let h=c[d.id]||null;u.push({trackId:d.id,name:d.name,createdAt:d.createdAt??null,archivedAt:d.archivedAt??null,live:!d.archivedAt,beat:d.beat??null,agents:OI(d),handoffs:(d.events||[]).filter(g=>g.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:Gt(d.workspace).map(g=>g.repo||cg.basename(g.base||"")),prs:o.filter(g=>g.trackId===d.id&&(!r.slug||g.repo===r.slug)).map(g=>({repo:g.repo,number:g.number,state:g.state??null,title:g.title??null,url:g.url??null})),mocks:s.filter(g=>g.trackId===d.id).length,views:a.filter(g=>g.trackId===d.id).length,runbooks:i.filter(g=>g.trackId===d.id).map(g=>({name:g.name,status:g.status})),ticket:h?{provider:h.provider,key:h.key,url:h.url??null}:null,usd:lg(m.totalUsd),lastActivityAt:ci(d)})}return{...dg(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as CI}from"node:child_process";import{promisify as MI}from"node:util";import{randomUUID as NI}from"node:crypto";var Kr=MI(CI),li=t=>new Promise(e=>setTimeout(e,t)),PI=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,mg=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,DI=250,BI=400,hg={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 Tu(t,e){let n=Date.now()+e;for(;;){if(await Ir(t))return!0;if(Date.now()>n)return!1;await li(DI)}}async function jI(t,e){let n=hg[e]||hg.default;n.escape&&(await kt(t,["Escape"],{kind:"swap-exit"}),await li(150)),n.wipe&&await Rr(t,va,{kind:"swap-exit"}),await Ta(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Be(e).submitDelayMs||0;await li(Math.max(r,250)),await kt(t,["Enter"],{kind:"swap-exit"})}async function LI(t,e){try{let{stdout:n}=await Kr(M,D("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function wg(t){await Kr(M,D("respawn-pane","-k","-t",t))}async function Au(t,e={}){if(!gt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await et(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||un(n),a=Number(e.exitTimeoutMs)||PI;await K({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Ir(t)&&(await jI(t,o),!await Tu(t,a)&&(i=!0,await K({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await wg(t),!await Tu(t,mg))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&qn(n),l=c?ia():null,u=await LI(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Kr(M,D("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Kr(M,D("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(g){throw l?ya(g,l):g}if(f&&!i&&(await wg(t),!await Tu(t,mg)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||NI(),m=d?n:Vc(n,p,t),h=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await sn(r.cwd),s==="codex"&&await br(r.cwd),s==="cursor"&&await kr(r.cwd),await Gn(t),await Kr(M,D("set-option","-t",t,"@bostrat-sid",p)),await Kr(M,D("set-option","-t",t,"@bostrat-agent",s)),await li(BI),await $t(t,m,{kind:"swap-launch"}),await K({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:h,forced:i,envRespawned:f}}import tR from"node:fs/promises";var FI=/^Async (?:agent|task) launched/i,UI=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 gg(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Wt(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
128
+ `)){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 f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let p=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let p=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(p?Pr(p):null),detail:p?Pr(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&FI.test(UI(d.content))&&s.add(d.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 ws from"node:fs";import WI from"node:fs/promises";import sr from"node:path";import qI from"node:os";import KI from"node:crypto";import GI from"node:zlib";var ui=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),le=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Gr=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function Tn(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 _u=t=>`"${String(t).replace(/"/g,'\\"')}"`;function HI(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 yg(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:le(`read ${Tn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Gr(r)}`:`Read ${Gr(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?Tn(e.path,n):null,s=e?.glob?` --glob ${_u(e.glob)}`:"",a=le(String(r),40);return{command:le(`grep ${_u(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?Tn(e.target_directory,n):null;return{command:le(`glob ${_u(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${le(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:le(`edit ${Tn(e.path,n)}`,140),description:`Edit ${Gr(e.path)}`}:null;case"Write":return e?.path?{command:le(`write ${Tn(e.path,n)}`,140),description:`Write ${Gr(e.path)}`}:null;case"Delete":return e?.path?{command:le(`delete ${Tn(e.path,n)}`,140),description:`Delete ${Gr(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>Tn(s,n)).filter(Boolean);return{command:le(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=HI(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:le(`apply_patch ${Tn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Gr(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:le(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var VI=process.env.BOSTRAT_CURSOR_CHATS||sr.join(qI.homedir(),".cursor","chats");function xg(t){let e=KI.createHash("md5").update(String(t)).digest("hex");return sr.join(VI,e)}function Sg(t){try{let e=JSON.parse(ws.readFileSync(sr.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 zI(t){let e=xg(t),n;try{n=ws.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=sr.join(e,o.name),a=sr.join(s,"store.db");if(!ws.existsSync(a))continue;let i=Sg(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 di(t,e){if(!t)return null;if(e){let n=sr.join(xg(t),e),r=sr.join(n,"store.db");if(ws.existsSync(r))return{id:e,dir:n,store:r,...Sg(n)||{createdAtMs:null,updatedAtMs:null}}}return zI(t)}function bg(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function JI(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=GI.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function YI(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 XI(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!ui.has(r.toolName))continue;let o=yg(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 ZI=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(`
129
+ `).trim():"",QI=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],kg=process.getBuiltinModule?.("node:sqlite"),Vr={turns:[],trailingCommands:[],model:null,requestIds:[]};async function Eu(t,e){if(!kg)return Vr;let{DatabaseSync:n}=kg,r;try{r=new n(t,{readOnly:!0})}catch{return Vr}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 Vr;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Vr}let a=s.lastUsedModel||null,i=s.latestRootBlobId?bg(r,s.latestRootBlobId):null;if(!i)return{...Vr,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of YI(i)){let m=JI(bg(r,p));if(!m)continue;let h=m.providerOptions?.cursor;if(h?.requestId&&d.push(h.requestId),h?.modelProviderMessageId&&d.push(h.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let S of m.content){let R=S?.providerOptions?.cursor?.modelName;R&&(f=R)}l.push(...XI(m.content,e).map((S,R)=>({...S,id:S.id||`${p}:${R}`}))),u.push(...QI(m.content));let g=ZI(m.content);if(!g)continue;let k=[...new Set(u)];u=[],c.push({id:p,text:g,tools:k.length?k:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return Vr}finally{try{r.close()}catch{}}}function vg(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(ws.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 Tg(t,e,{limit:n=80}={}){let r=di(t,e);if(!r)return[];let{turns:o,model:s}=await Eu(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=vg(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 Ag(t,e){let n=di(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Eu(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 _g(t,e,{limit:n=80}={}){let r=di(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await Eu(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=vg(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:hn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:hn(u.command,u.description),ts:i+1});return l}async function Eg(t,e){let n=di(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await WI.stat(n.store)).mtimeMs)}catch{return null}}var zr=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}},Iu=class extends zr{transcript(e){return!e?.cwd||!e?.sessionId?null:De(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?el(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?km(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?gg(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await tR.stat(n)).mtimeMs)}catch{return null}}},Ru=class extends zr{async readMessages(e,n){return Tg(e.cwd,e.sessionId,n)}async readCommands(e,n){return _g(e.cwd,e.sessionId,n)}async updatedAt(e){return Eg(e.cwd,e.sessionId)}},$u=class extends zr{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return Tm(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return Am(e.cwd,this.sessionId(e),n)}async updatedAt(e){return _m(e.cwd,this.sessionId(e))}},eR=new zr("unknown"),Ig=new Map;function Ou(t){return Ig.set(t.agent,t),t}function gs(t){return Ig.get(t)||eR}Ou(new Iu("claude",wl));Ou(new Ru("cursor",ui));Ou(new $u("codex",zo));var Cu=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function Mu(t,e={}){return(await gs(t.agent).readMessages(t,e)).map(Cu(t))}async function Rg(t,e={}){return(await gs(t.agent).readCommands(t,e)).map(Cu(t))}async function $g(t,e={}){return(await gs(t.agent).readJobs(t,e)).map(Cu(t))}var Mg=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Du=/Chat about this/i,ar=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Og=/^(type something|chat about this|submit)\b/i,ir=/^[─—-]{6,}\s*$/,Bu=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function cr(t){let e=t.search(Bu);return(e>=0?t.slice(0,e):t).trim()}function nR(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function rR(t){let e=String(t).trim();return!!(!e||ir.test(t)||Bu.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function oR(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=sR(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function sR(t,e){let n=String(e).toLowerCase(),r=t.findIndex(c=>!c.answered),o=r>=0?r:t.length-1;if(!n)return o;let s=t.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(d=>d.length>3&&n.includes(d)).length;if(u)return u;let f=l.slice(0,8).trim();return f.length>=4&&n.includes(f)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function Cg(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Ng=/^\s*Switch model\?\s*$/m;function aR(t,e=""){let n=String(t);if(!Ng.test(n))return null;let r=n.replace(/\r/g,"").split(`
130
+ `),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 f=r[u].match(ar);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!ir.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:cr(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 Pg=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,iR=/^\s*[─—▔-]{6,}\s*$/;function cR(t,e=""){let n=String(t).replace(/\r/g,"").split(`
131
+ `),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Pg.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(ar);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&&!(iR.test(n[c])||ir.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:cr(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 lR(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
132
+ `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(ar);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 Nu=/Resume from summary/i,uR=/^\s*This session is\b/;function dR(t,e=""){let n=String(t);if(!Nu.test(n))return null;let r=n.replace(/\r/g,"").split(`
133
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if(uR.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 f=r[u].match(ar);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!ir.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>Nu.test(u.label)))return null;let i=s.map(u=>({label:cr(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 lr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function ju(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(ar);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 Dg=/Choose the text style that looks best with your terminal/,Bg=/Let's get started\./;function fR(t,e=""){let n=String(t);if(!Dg.test(n)||!Bg.test(n)||lr.test(n))return null;let r=n.replace(/\r/g,"").split(`
134
+ `),o=ju(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:cr(s.label),description:""}))}]}:null}var jg=/Select login method:/;function pR(t,e=""){let n=String(t);if(!jg.test(n)||lr.test(n))return null;let r=n.replace(/\r/g,"").split(`
135
+ `),o=ju(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:cr(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 Lu=/Paste code here if prompted/,mR=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function hR(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!mR.test(o)||Lu.test(t[r]))break;n+=o}return n}function wR(t,e=""){let n=String(t);if(!Lu.test(n)||lr.test(n))return null;let r=n.replace(/\r/g,"").split(`
136
+ `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=hR(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 Pu=/OAuth error:/,gR=/Press Enter to retry\./;function yR(t,e=""){let n=String(t);if(!Pu.test(n)||!gR.test(n)||lr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
137
+ `)].reverse().find(a=>Pu.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 Lg=/Quick safety check: Is this a project you created or one you trust/;function bR(t,e=""){let n=String(t);if(!Lg.test(n)||lr.test(n))return null;let r=n.replace(/\r/g,"").split(`
138
+ `),o=ju(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:cr(s.label),description:""}))}]}:null}var Fg=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function kR(t,e=""){let n=String(t);if(lr.test(n))return null;let r=n.replace(/\r/g,"").split(`
139
+ `),o=-1;for(let l=r.length-1;l>=0;l--)if(r[l].trim()!==""){o=l;break}if(o<0)return null;let s=r[o].match(Fg);if(!s)return null;let a=[];for(let l=o-1;l>=0&&a.length<3;l--){let u=r[l].trim();if(!u){if(a.length)break;continue}if(ir.test(r[l])||Bu.test(u))break;a.unshift(u)}let i=s[1].trim();i&&a.push(i);let c=a.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${c}`.slice(0,200),questions:[{question:c,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function xR(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Du.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 SR(t,e=""){let n=String(t);if(!Mg.test(n)||!Du.test(n))return null;let r=n.replace(/\r/g,"").split(`
140
+ `),o=xR(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let A=i;A<c;A++){let C=r[A].match(ar);C&&(u<0&&(u=A),l.push({line:A,cursor:!!C[1],checked:C[3]!=null&&C[3]!==" ",hasCheckbox:C[3]!=null,label:C[4].trim(),desc:""}))}if(!l.length)return null;for(let A of l)for(let C=A.line+1;C<Math.min(r.length,A.line+4)&&!(ar.test(r[C])||ir.test(r[C]));C++){let F=r[C].trim();if(!(!F||rR(r[C])||Og.test(F))){A.desc=nR(F);break}}let f=l.filter(A=>!Og.test(A.label));if(!f.length)return null;let d=f.some(A=>A.hasCheckbox),p=Math.max(0,f.findIndex(A=>A.cursor)),m=s>=0?s+1:0,h=[];for(let A=m;A<u;A++){let C=r[A].trim();C&&!ir.test(r[A])&&!/[☐☒]/.test(r[A])&&h.push(C)}let g=h.join(" ")||(s>=0?Cg(r[s]):"")||"The agent is asking a question",k=s>=0?oR(r[s],g):null,S=k?.currentTabLabel||(s>=0?Cg(r[s]):""),R=f.map(A=>({label:cr(A.label),description:A.desc})),$=R.map(A=>A.label).join("|"),O=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${S}:${g}:${$}`.slice(0,240),N=k?`claude:ask:${k.tabs.map(A=>A.label).join("|")}`.slice(0,240):O;return{provider:"claude",box:e,multiSelect:d,cursorIndex:p,promptId:O,groupId:N,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:g,header:S,multiSelect:d,options:R}]}}function Jr(t,e){let n=String(e||"");return n?t==="claude"?Mg.test(n)&&Du.test(n)||Ng.test(n)||Nu.test(n)||Pg.test(n.trimEnd().split(`
141
+ `).pop()||"")?!0:lr.test(n)?!1:Dg.test(n)&&Bg.test(n)||jg.test(n)||Lu.test(n)||Pu.test(n)||Lg.test(n)||Fg.test(n.trimEnd().split(`
142
+ `).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 ur(t,e){return!t||!e?null:t.agent==="claude"?lR(e,t.name)||SR(e,t.name)||aR(e,t.name)||dR(e,t.name)||yR(e,t.name)||wR(e,t.name)||pR(e,t.name)||fR(e,t.name)||bR(e,t.name)||kR(e,t.name)||cR(e,t.name):t.agent==="cursor"||t.agent==="codex"?tm(t.agent,e,t.name):null}async function Fu(t){if(!t)return null;let e=await fn(t.name).catch(()=>"");return ur(t,e)}function Ug(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 Hg(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)}var vR=[{provider:"claude",id:"fullscreen-upsell",match(t,e){let n=t?.questions?.[0],r=(n?.options||[]).map(s=>String(s.label||"").trim());if(r.length!==2||!/^Yes, try it$/.test(r[0])||!/^Not now$/.test(r[1]))return!1;let o=`${n?.question||""}
143
+ ${String(e||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];function fi(t,e,n){if(!t||!e)return null;for(let r of vR)if(r.provider===t.agent)try{if(r.match(e,n))return r}catch{}return null}var TR=5e3,Wg=5,qg=3600*1e3,Uu=new Map;function AR(t){let e=Uu.get(t);return e||(e={sentAt:[],last:0,emitted:new Set},Uu.set(t,e)),e}function Kg(t,e,n=Date.now()){let r=AR(t);if(r.sentAt=r.sentAt.filter(s=>n-s<qg),r.sentAt.length>=Wg)return{action:"give-up"};if(n-r.last<TR)return{action:"wait"};r.last=n,r.sentAt.push(n);let o=!r.emitted.has(e);return o&&r.emitted.add(e),{action:"send",first:o}}function Gg(t,e=Date.now()){let n=Uu.get(t);return n?n.sentAt.filter(r=>e-r<qg).length>=Wg:!1}function Yr(t){return typeof t=="string"?t.trim():""}import{execFile as _R}from"node:child_process";import{promisify as ER}from"node:util";var Wu=ER(_R),Vg=64*1024*1024;async function bt(t,e,{input:n}={}){let r=Wu("git",["-C",t,...e],{maxBuffer:Vg});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function pi(t){if(!t)return!1;try{let{stdout:e}=await bt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function zg(t){let{stdout:e}=await bt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function IR(t){try{return await bt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function ys(t,e="bostrat: subtask snapshot"){let{stdout:n}=await bt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Wu("git",["-C",t,...a],{maxBuffer:Vg,env:o,...i});try{let a=await IR(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 Wu("rm",["-f",r])}catch{}}}async function qu(t,e){let{stdout:n}=await bt(t,["status","--porcelain"]);return n.trim()?(await bt(t,["add","-A"]),await bt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function Ku(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await bt(t,["status","--porcelain"]),r=n.split(`
144
+ `).filter(Boolean);return e&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function Gu(t,e){if(!e)return 0;try{let{stdout:n}=await bt(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Xr(t,e,n,r){return await bt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Jg(t,e){try{return await bt(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function Yg(t,e,n){return await bt(t,["worktree","add",e,n]),e}async function mi(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await bt(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 bt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function Xg(t){await bt(t,["init","-q"]),await bt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function re(t,e){try{await bt(t,["worktree","remove","--force",e])}catch{try{await bt(t,["worktree","prune"])}catch{}}}function RR(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 Vu(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function zu(t,e,n,r){let{stdout:o}=await bt(t,Vu(["diff","--shortstat",e,n],r));return RR(o)}async function Ju(t,e,n,r){let{stdout:o}=await bt(t,Vu(["diff","--name-only",e,n],r));return o.split(`
145
+ `).map(s=>s.trim()).filter(Boolean)}async function Yu(t,e,n,r){let{stdout:o}=await bt(t,Vu(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Hu(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Hu(t,o,["--3way","--whitespace=nowarn"]),a=await $R(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Hu(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Hu(t,e,n){try{return await bt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function $R(t){try{let{stdout:e}=await bt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
146
+ `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import Xu from"node:fs/promises";import Qg from"node:path";import OR from"node:os";var ty=process.env.BOSTRAT_STATE_DIR||Qg.join(process.env.HOME||OR.tmpdir(),".bostrat"),ey=()=>Qg.join(ty,"usage-caps.json"),ny=.8;function ry(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(e.getDate()-(e.getDay()+6)%7),e.getTime()}function oy(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(1),e.getTime()}function sy(t){let e=new Date(ry(t));return e.setDate(e.getDate()+7),e.getTime()}function ay(t){let e=new Date(oy(t));return e.setMonth(e.getMonth()+1),e.getTime()}var Zr={weekly:{startOf:ry,nextStart:sy,capKey:"weeklyUsd"},monthly:{startOf:oy,nextStart:ay,capKey:"monthlyUsd"}},ue=null,Qu=!1,ze={weekly:0,monthly:0},dr=new Map,Qr=new Set,Zu=null,CR=t=>{let e=dr.get(t);return e||(e={weekly:0,monthly:0},dr.set(t,e)),e};function wi(t=Date.now()){for(let[e,n]of Object.entries(Zr)){let r=n.startOf(t);if(r!==ze[e]){ze[e]=r;for(let o of dr.values())o[e]=0;for(let o of Qr)o.includes(`:${e}:`)&&!o.endsWith(`:${r}`)&&Qr.delete(o)}}}function iy(t,e){let n=ue?.caps?.[t]?.[Zr[e].capKey];return Number.isFinite(n)&&n>0?n:null}function to(t,e=Date.now()){if(!t||!Qu||!ue?.caps)return null;wi(e);let n=dr.get(t);for(let r of Object.keys(Zr)){let o=iy(t,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:t,window:r,limitUsd:o,spentUsd:hi(s),resetsAt:Zr[r].nextStart(e)}}return null}function cy(t,e){let n=`hit:${t}:${e.provider}:${e.window}:${ze[e.window]}`;return Qr.has(n)?!1:(Qr.add(n),!0)}function Zg(t,e,n,r=Date.now()){if(!t||!e)return;wi(r);let o=CR(t);for(let[s,a]of Object.entries(Zr))n>=ze[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function MR(t,e,n=Date.now()){if(!ue?.caps||!Zu)return;let r=dr.get(t);if(r)for(let[o,s]of Object.entries(Zr)){let a=iy(t,o);if(a==null)continue;let i=r[o]||0;if(i<a*ny||i>=a)continue;let c=`warn:${t}:${o}:${ze[o]}`;if(!Qr.has(c)){Qr.add(c);try{let l=await Zu(e);if(!l)continue;await I(l,{type:"usage.cap.warning",actor:"system",provider:t,payload:{provider:t,window:o,limitUsd:a,spentUsd:hi(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function ly(t){let e={};for(let[n,r]of Object.entries(t||{})){if(typeof n!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n))throw new Error(`bad provider key "${n}"`);let o={};for(let s of["weeklyUsd","monthlyUsd"]){let a=r?.[s];if(a==null||a==="")continue;let i=Number(a);if(!Number.isFinite(i)||i<=0)throw new Error(`${n}.${s} must be a positive number or null`);o[s]=i}Object.keys(o).length&&(e[n]=o)}return e}async function gi(t,{source:e="local",updatedAt:n=Date.now()}={}){ue={caps:ly(t),updatedAt:n,source:e};try{await Xu.mkdir(ty,{recursive:!0}),await Xu.writeFile(ey(),JSON.stringify(ue,null,2))}catch{}return bs()}function bs(t=Date.now()){wi(t);let e=new Set([...dr.keys(),...Object.keys(ue?.caps||{})]),n={};for(let r of e){let o=dr.get(r);n[r]={weeklyUsd:hi(o?.weekly||0),monthlyUsd:hi(o?.monthly||0)}}return{caps:ue?.caps||{},configured:!!ue,seeded:Qu,updatedAt:ue?.updatedAt??null,spent:n,windows:{weekly:{start:ze.weekly,resetsAt:sy(t)},monthly:{start:ze.monthly,resetsAt:ay(t)}},warnPct:ny}}async function uy({listTracks:t,getTrack:e}={}){Zu=e||null,wi();try{let o=JSON.parse(await Xu.readFile(ey(),"utf8"));ue={caps:ly(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{ue=null}let n=Date.now();qt.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=je(o);Zg(o.provider||"unknown",s,o.ts??Date.now()),MR(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(ze.weekly,ze.monthly);for(let o of await t?.()||[]){let s;try{s=await P(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||Zg(a.provider||"unknown",je(a).usd,i,n)}}return Qu=!0,bs()}var hi=t=>Math.round(t*1e4)/1e4;import NR from"node:fs/promises";import PR from"node:os";import dy from"node:path";var yi=t=>new Promise(e=>setTimeout(e,t)),DR=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,BR=300,hy=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 5"},{alias:"sonnet",label:"Sonnet 5"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],jR=new Set([...hy.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),LR=[{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.6-terra",label:"GPT-5.6 Terra"},{alias:"gpt-5.6-luna",label:"GPT-5.6 Luna"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}],wy=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),gy=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{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.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}];function yy(t){return t==="codex"?LR:t==="cursor"?gy:hy}function fy(t){let e=String(t||"").split(`
147
+ `),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 py(t){let e=String(t||"").split(`
148
+ `),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 FR(t){let e=String(t||"").split(`
149
+ `);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 my(t){let e=String(t||"").split(`
150
+ `),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 by(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&&!wy.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=gy.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!jR.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ks(t,e,{timeoutMs:n=DR}={}){let r=Date.now()+n;for(;;){let o=await wt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await yi(BR)}}async function UR(t,e){return await $t(t,`/model ${e.model}`,Be("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function HR(t,e){await Sa(t),await $t(t,"/model",{enter:!1}),await yi(250),await kt(t,["Enter"],{kind:"model-switch"});let n=await ks(t,fy);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await kt(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=FR(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await $t(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await ks(t,i=>py(i)||!fy(i)&&o(i)&&{done:o(i)});if(!s)throw await kt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?wy.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 kt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await $t(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await ks(t,i=>!py(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 WR(){let t=dy.join(process.env.CURSOR_CONFIG_DIR||dy.join(PR.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await NR.readFile(t,"utf8"))?.model||null}catch{return null}}async function qR(t,e){await Sa(t),await Ta(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await ks(t,my),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 ks(t,my,{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 kt(t,["Down"],{kind:"model-switch"}),await yi(250)}if(!n||!s||!o(s)){await kt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await kt(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 kt(t,["Enter"],{kind:"model-switch"}),await yi(800);let a=await WR();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 ky(t,e,n){return t==="codex"?HR(e,n):t==="cursor"?qR(e,n):UR(e,n)}var Zt=t=>t||null;function ki(t){let e=t||{},n=KR(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function KR(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Zt(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:Zt(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:Zt(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:Zt(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,checkpoint:t.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Zt(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:Zt(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:Zt(t.agent),payload:{...eo(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Zt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...eo(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Zt(t.agent),payload:{change:"subtask-resumed",...eo(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",...eo(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...eo(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Zt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...eo(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Zt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var bi=Promise.resolve(),ed=t=>(bi=bi.then(t).catch(()=>{}),bi),Sy=()=>bi;function vy(t,e){let n=ki(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;ed(()=>I(r,n))}var xy=new Set;async function Ty(t){if(!(!t?.id||xy.has(t.id))){xy.add(t.id);try{let e=await P(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=ki(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(t,o)}}catch{}}}function eo(t){let{type:e,at:n,...r}=t;return r}var td=new Map;function Ay(t,e,n){!n?.promptId||td.get(t.id)===n.promptId||(td.set(t.id,n.promptId),ed(()=>I(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Zt(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 nd(t,e,n,r={}){td.delete(t.id),ed(()=>I(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Zt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as C$}from"node:crypto";var Ey=/(?<!\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,Iy=/(?<!\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,Ry=/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,$y=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|clean|interactive)\s)rebase(?!s\b)(?:\s+(?:it|this|that|them|(?:this|that|the)\s+branch))?(?:\s+(?:on(?:to)?|against|atop|over)\s+\S+)?/.source,GR=new RegExp(`\\b(?:${Ey}|${Iy}|${Ry}|${$y})\\b`,"i"),VR=/\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,zR=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,JR=90;function YR(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function Oy(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of YR(n).reverse()){if(!GR.test(r))continue;let o=r.endsWith("?")&&r.length<=JR&&zR.test(r);if(VR.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var Cy=["commit","rebase","push","pr"],XR=new RegExp(`\\b(?:${Ey})\\b`,"i"),ZR=new RegExp(`\\b(?:${$y})\\b`,"i"),QR=new RegExp(`\\b(?:${Iy})\\b`,"i"),t$=new RegExp(`\\b(?:${Ry})\\b`,"i"),e$=/\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 My(t){let e=String(t||""),n=[];XR.test(e)&&n.push("commit"),ZR.test(e)&&n.push("rebase"),QR.test(e)&&n.push("push");let r=e$.test(e);!r&&t$.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 xi(t,{branch:e=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(t),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=e?` on this branch (${e})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${e?` (${e})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${e?` (${e})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}var n$=3e4;function Ny(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<=n$&&n.push({box:r.box,from:r.from,to:e+1e12}),n}var r$=2500;function Py(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+r$||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}var _y={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function Dy(t){let e=(Array.isArray(t)?t:[]).filter(r=>_y[r]).map(r=>_y[r]);return`The ship you were given the go-ahead for${e.length?` (${e.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}import{execFile as c$}from"node:child_process";import{promisify as l$}from"node:util";import o$ from"node:fs";import rd from"node:fs/promises";import Fy from"node:path";import s$ from"node:os";var Uy=process.env.BOSTRAT_STATE_DIR||Fy.join(process.env.HOME||s$.tmpdir(),".bostrat"),Si=Fy.join(Uy,"github-metrics.json"),Hy=60*6e4,By=5*6e4,a$=6e4,no=Date.now(),Je=new Map,An=null,fr=0,jy=0,vi=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(o$.readFileSync(Si,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Hy?(no=t.windowStart,Je=new Map(Object.entries(t.byCaller||{})),An=t.lastHour||null):vi(new Map(Object.entries(t.byCaller||{})))&&(An={start:t.windowStart,total:vi(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var Ly=Promise.resolve();function Wy(t,e=!1){if(!e&&t-jy<a$)return;jy=t;let n=JSON.stringify({windowStart:no,byCaller:Object.fromEntries(Je),lastHour:An});Ly=Ly.then(()=>rd.mkdir(Uy,{recursive:!0})).then(()=>rd.writeFile(`${Si}.tmp`,n)).then(()=>rd.rename(`${Si}.tmp`,Si)).catch(()=>{})}function qy(t){t-no<Hy||(An={start:no,total:vi(Je),byCaller:Object.fromEntries(Je)},An.total&&console.log(`[github] ${An.total} calls last hour`,An.byCaller),no=t,Je=new Map,Wy(t,!0))}function Ye(t="other",e=Date.now()){qy(e),Je.set(t,(Je.get(t)||0)+1),Wy(e)}function Ky(t=Date.now()){return qy(t),{windowStart:no,total:vi(Je),byCaller:Object.fromEntries(Je),lastHour:An,backoffUntil:fr>t?fr:null}}var i$=/rate limit|too quickly|abuse detection|HTTP 429/i;function _n(t){return i$.test(String(t?.message||t||""))}function En(t,e=Date.now()){return fr<=e&&console.warn(`[github] rate limited \u2014 backing off ${By/6e4} min:`,String(t?.message||t||"").slice(0,200)),fr=Math.max(fr,e+By),fr}function Ti(t=Date.now()){return Math.max(0,fr-t)}var Gy=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,xs=new Map;function Ai(t,e,n){let{state:r,reason:o}=al(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of xs)a-c.at>=Gy()&&xs.delete(i);xs.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=xs.get(t);return s?Date.now()-s.at>=Gy()?(xs.delete(t),!1):s.busy:!1}async function od(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await wt(t,{lines:Rt}).catch(()=>"");if(!Ai(t,e,r))return om(e,r)?{working:!0,prompted:!1}:n;if(Jr(e,r)){let o=await fn(t).catch(()=>r),s=ur({name:t,agent:e},o);if(s)return!fi({name:t,agent:e},s,o)||Gg(t)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function _i(t,e){return(await od(t,e)).working}var Vy=l$(c$),zy=[4e3,15e3,45e3],u$=2500,d$=5*6e4;async function sd(t,e,{midTurn:n=_i,pollMs:r=u$,capMs:o=d$}={}){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 f$=15e3,p$=9e4,m$=2e3,h$=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,w$=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,g$=/\s--web\b/,Jy=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,y$=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,b$=/^track\/.+-([0-9a-f]{8})$/;function k$(t,e){let n=String(t||"").match(b$);return!!n&&n[1]!==e}function ad(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Yy(t){let e=String(t||""),n=e.match(h$);if(!n)return null;let r=ad(e,n.index);return g$.test(r)?null:{repo:r.match(Jy)?.[1]??null,head:r.match(y$)?.[1]??null}}var x$=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function Xy(t){let e=String(t||""),n=e.match(w$);if(!n)return null;let r=ad(e,n.index),o=ad(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("-")){x$.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Jy)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var S$="number,url,title,headRefName,createdAt";async function v$(t,e,{exclude:n=()=>!1}={}){let r=async i=>{Ye("observer");let{stdout:c}=await Vy("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",S$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw _n(l)&&En(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=e-f$&&!n(c)),s=await we({slug:t}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function T$(t,e){if(e)return[e];let n=[];for(let r of Zy(t)){let o=await rt(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Ne().catch(()=>[])){let o=await rt(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Zy(t){let e=t.workspace;return e?.kind==="multi"?(e.repos??[]).map(n=>n?.base).filter(Boolean):[e?.base||t.cwd].filter(Boolean)}async function A$(t,e,n){let r=t.workspace?.branch,o=e?.head??null,s=c=>r&&c===r||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||k$(c.headRefName,t.id),i=[];for(let c of await T$(t,e?.repo??null)){let l;try{l=await v$(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await Lt(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===t.id&&f.announced)return{settled:!0};if(f&&f.trackId!==t.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==t.id||f.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}var In=new Set;function Qy(t,e,{delays:n=zy,turnEnd:r}={}){let o=Yy(e.command);if(!o)return null;let s=!!Xy(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(In.has(a))return null;In.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>setTimeout(h,l));let u=await A$(t,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await Xt({repo:f,number:d.number,trackId:t.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await fs(f,d.number))?null:(await sd(e.box,e.agent,r),await I({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:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{In.delete(a)}})();return c.catch(()=>In.delete(a)),c}var _$="number,url,title,headRefName,state";function tb(t,e,{delays:n=zy,turnEnd:r,openedWait:o}={}){let s=Xy(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(In.has(a))return null;In.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let m=Zy(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of m){let g=await rt(h).catch(()=>null);g&&!f.includes(g)&&f.push(g)}}if(l||=t.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,h)=>{let g=await Lt(c,m.number);return g&&g.trackId!==t.id?null:(g||await Xt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await ps(c,m.number),h&&((await P(t).catch(()=>[])).some(R=>R.type==="pr.state.changed"&&(R.payload?.change==="auto-armed"||R.payload?.change==="merged")&&R.payload?.number===m.number)||(await sd(e.box,e.agent,r),await I({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:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(O=>setTimeout(O,m));let h=[];for(let O of f)try{Ye("observer");let{stdout:N}=await Vy("gh",["pr","view",String(l),"--repo",O,"--json",_$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await we({slug:O})}}),A=JSON.parse(N||"null");A?.number&&h.push({slug:O,pr:A})}catch(N){_n(N)&&En(N)}let g=h.find(O=>O.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!g)continue;c=g.slug;let k=g.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return d(k,!0);continue}let S=await Lt(c,k.number);if(S&&S.trackId!==t.id)return null;S||await Xt({repo:c,number:k.number,trackId:t.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await sd(e.box,e.agent,r);let R=await P(t).catch(()=>[]);if(R.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="merged"&&O.payload?.number===k.number))return null;let $=O=>O.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="opened"&&N.payload?.number===k.number),J=0;if(!$(R)){if(Yy(e.command)&&await fs(c,k.number)){let O=Date.now();J=O+1,await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:O,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await Lt(c,k.number))?.announced){let O=o?.capMs??p$,N=o?.pollMs??m$,A=Date.now()+O;for(;Date.now()<A&&(await new Promise(C=>setTimeout(C,N)),!$(await P(t).catch(()=>[]))););}}return await I({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(),J),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await $n(c,k.number).catch(()=>{}),await Rn(c,k.number).catch(()=>{}),await vn(c,k.number,"merged"),k}return p?d(p,!1):null}finally{In.delete(a)}})();return i.catch(()=>In.delete(a)),i}import E$ from"node:fs/promises";import cd from"node:path";import I$ from"node:os";var R$=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/;function id(t,e){let n=new RegExp(`\\b${e}="([^"]*)"`).exec(t);return n?n[1]:null}function Ei(t){let e=R$.exec(String(t||""));if(!e)return null;let[,n,r]=e,o=id(n,"from"),s=o?o.replace(/^uds:/,""):null,a=eb(s);return{socket:s,pid:a,name:id(n,"from-name"),mode:id(n,"from-mode"),text:r.trim()}}function eb(t){let e=/(\d+)\.sock$/.exec(String(t||""));return e?Number(e[1]):null}function $$(){return process.env.CLAUDE_CONFIG_DIR?cd.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):cd.join(I$.homedir(),".claude","sessions")}async function O$(t){let e=eb(t);if(!e)return null;try{let n=await E$.readFile(cd.join($$(),`${e}.json`),"utf8"),r=JSON.parse(n);return{pid:e,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function nb(t,e){let n=await O$(t?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof e=="function")try{o=await e(n?.sessionId??null,r)??null}catch{o=null}return{socket:t?.socket??null,pid:n?.pid??t?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??t?.name??null,mode:t?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}function Ss(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function rb(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function ob(t){return`job|${t.box}|id:${t.id}`}var ld=new Map,Ii=new Map;async function ud(t){return ld.has(t.id)?ld.get(t.id):(Ii.has(t.id)||Ii.set(t.id,P(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(Ss({role:o,sourceId:r.payload.sourceId})),n.keys.add(Ss({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(Ss({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Ss({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(rb({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(ob({id:r.payload.id,box:r.boxId}));return ld.set(t.id,n),n}).finally(()=>Ii.delete(t.id))),Ii.get(t.id))}async function sb(t,e){try{let n=(await ud(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Ss(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?Ei(r.text):null;if(s){await I(t,{type:"peer.message",actor:"peer",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:s.text,ts:r.ts,keyText:String(r.text||"").slice(0,40),from:await nb(s,Ri),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}if(await I(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 a=Oy(r.text);if(a){let{offered:i,toPr:c}=My(a),l;if(t.workspace?.kind==="multi"){let u=await vs(t).catch(()=>[]);l=u.length?u.map(f=>({name:f.name,slug:f.repo??null})):void 0}await I(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:C$(),ts:r.ts,excerpt:a,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:i||void 0,toPr:c||void 0,repos:l}})}}}}catch{}}async function ab(t,e){try{let n=await ud(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=rb(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await I(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}}),Qy(t,o),tb(t,o))}}catch{}}async function ib(t,e){try{let n=await ud(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=ob(o);n.keys.has(s)||(n.keys.add(s),await I(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{}}function M$(t){let e=[],n=null;for(let r of String(t||"").split(`
151
+ `)){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 dd=new Map;async function N$(t,e){let n=`${t.id}:${e.name}`;if(!dd.has(n)){let r=await P(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);dd.set(n,o)}return dd.get(n)}async function cb(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await zn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await N$(t,e),o=M$(await Wt(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 I(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:yn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await I(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 $i from"node:fs";import pd from"node:path";import P$ from"node:os";var D$=process.env.BOSTRAT_STATE_DIR||pd.join(process.env.HOME||P$.tmpdir(),".bostrat"),ub=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,B$="https://cursor.com/api/dashboard/get-filtered-usage-events",lb=100;async function j$({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(B$,{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:lb})});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<lb)break}return r}function L$(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 F$=5*6e4,On={at:0,rows:null,error:null},Oi=()=>pd.join(D$,"cursor-usage.json");function db(){try{return JSON.parse($i.readFileSync(Oi(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function U$(t){try{$i.mkdirSync(pd.dirname(Oi()),{recursive:!0});let e=Oi()+".tmp";$i.writeFileSync(e,JSON.stringify(t)),$i.renameSync(e,Oi())}catch{}}var H$=5e3;async function W$({fetchImpl:t,now:e=Date.now()}={}){if(On.rows&&e-On.at<F$)return On.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=db(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await j$({sinceMs:o,cookie:n,fetchImpl:t});On={at:e,rows:s.map(L$).filter(Boolean),error:null}}catch(s){On={at:e,rows:null,error:String(s?.message||s)}}return On.rows}var md=()=>({enabled:ub(),lastPollAt:On.at||null,error:On.error}),fd=new Map;async function q$(t,e){let n=`${t.id}:${e.name}`;if(!fd.has(n)){let r=await P(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);fd.set(n,o)}return fd.get(n)}async function fb(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!ub())return;let r=await W$({fetchImpl:n});if(!r||!r.length)return;let o=await Ag(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await q$(t,e),a=db(),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",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=yn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await I(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...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}})}U$({sinceMs:c,seen:[...i].slice(-H$)})}catch{}}import{execFile as J$}from"node:child_process";import{promisify as Y$}from"node:util";import pb from"node:fs/promises";import mb from"node:path";import K$ from"node:os";var hb=process.env.BOSTRAT_STATE_DIR||mb.join(process.env.HOME||K$.tmpdir(),".bostrat"),G$=mb.join(hb,"github-latency.ndjson"),V$=100,Ci=[],hd=new Map;function Mi({change:t,repo:e=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:t,repo:e,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};Ci.push(l),Ci.length>V$&&Ci.shift();let u=hd.get(t)||{count:0,measured:0,totalMs:0,maxMs:0};return u.count+=1,c!=null&&(u.measured+=1,u.totalMs+=c,u.maxMs=Math.max(u.maxMs,c)),hd.set(t,u),z$(l),l}async function z$(t){try{await pb.mkdir(hb,{recursive:!0}),await pb.appendFile(G$,JSON.stringify(t)+`
152
+ `)}catch{}}function wb(){let t={};for(let[e,n]of hd)t[e]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:t,recent:Ci.slice(-20)}}async function X$(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([oo("git",["-C",t,"rev-parse","HEAD"]),oo("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await I({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var oo=Y$(J$),Z$={timeout:3e4,maxBuffer:8*1024*1024};async function oe(t,e,n={}){let{caller:r="prs",...o}=n,s=await we({cwd:t});Ye(r);try{let{stdout:a}=await oo("gh",e,{cwd:t,...Z$,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw _n(a)&&En(a),a}}var Ni=null;async function yd(){if(Ni&&Date.now()-Ni.at<6e4)return Ni.data;let t;try{Ye("auth");let e=await oo("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"||e?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await Oo();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Ni={at:Date.now(),data:t},t}async function Q$(t){try{let{stdout:e}=await oo("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function yb(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r=t.completedAt||t.createdAt||null,o;return t.__typename==="StatusContext"?o=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?o="pass":o="fail",{name:e,state:o,url:n,completedAt:r}}function wd(t){let e=(t||[]).map(yb),n=e.filter(s=>s.state==="pending").length,r=e.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=e.length&&!n&&r.length?Math.max(...r):null;return{total:e.length,passed:e.filter(s=>s.state==="pass").length,failed:e.filter(s=>s.state==="fail").length,pending:n,failing:e.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}var bd="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",bb=bd.replace("statusCheckRollup,","");function kd(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function kb(t,e){if(!e)return null;let n=await rt(t);if(!n)return null;try{let r=await oe(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 xb(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,completedAt:e.status==="completed"&&e.updated_at||null}))}async function Sb(t,e){let n=await kb(t,e);return n===null?{...wd([]),unavailable:!0}:wd(xb(n))}function Pi(t,e=wd(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 tO=9e4,ro=new Map;function eO(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){ro.delete(s);continue}r.add(s),ro.has(s)||ro.set(s,n),o.mergeabilityPending=n-ro.get(s)<tO}for(let o of ro.keys())o.startsWith(`${t}#`)&&!r.has(o)&&ro.delete(o);return e}async function so(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await oe(n.path,["pr","view",String(r),"--json",bd]);return Pi(JSON.parse(o))}catch(o){if(!kd(o))throw o;let s=await oe(n.path,["pr","view",String(r),"--json",bb]),a=JSON.parse(s);return Pi(a,await Sb(n.path,a.headRefOid))}}var gb=(t,e)=>t.number-e.number;async function As(t,{caller:e="prs"}={}){try{let n=await oe(t,["pr","list","--json",bd,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>Pi(r)).sort(gb)}catch(n){if(!kd(n))throw n;let r=await oe(t,["pr","list","--json",bb,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Pi(o,await Sb(t,o.headRefOid))))).sort(gb)}}var nO=3e4,rO=1500,Ts=new Map;function Ce(){Ts.clear()}async function oO(t,e){if(!t)return e;for(let n of e){let r=await Lt(t,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await oi(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await ai(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function vb(){let t=await yd();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Ne())await Q$(a.path)&&e.push(a);let n=Date.now(),r=Ti(n)>0,o=await Promise.all(e.map(async a=>{let i=Ts.get(a.path),c=i?.pending?rO:nO;if(i&&(r||n-i.at<c))return i.entry;try{let l=await rt(a.path).catch(()=>null),u=eO(a.path,await As(a.path,{caller:"tab"}),n);if(await oO(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(h=>h.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Mi({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return Ts.set(a.path,{at:Date.now(),entry:f,pending:d}),f}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Oe(l)}}})),s=new Set(e.map(a=>a.path));for(let a of Ts.keys())s.has(a)||Ts.delete(a);return{auth:t,repos:o}}async function Xe(t){let e=(await Ne()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function pr(t){return Ro(t)}var _s=new Set(["squash","merge","rebase"]);async function ao(t,e,{method:n="squash"}={}){let r=await Xe(t);if(!_s.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 we({cwd:r.path})).GH_TOKEN){let a=await Ic().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await oe(r.path,s)}catch(a){throw new Error(Oe(a))}finally{Ce()}return{ok:!0,number:o}}async function Es(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await oe(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Oe(s))}finally{Ce()}let o=await rt(n.path).catch(()=>null);return o&&await ng(o,r).catch(()=>{}),{ok:!0,number:r}}async function xd(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await oe(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Oe(o))}finally{Ce()}return{ok:!0,number:r}}var sO=2500,aO=9e4,iO=t=>new Promise(e=>setTimeout(e,t));function Sd(t){return t.green&&!t.isDraft}function cO(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})`}function Tb(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:Sd(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Ab(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var gd=new Map;function _b(){let t={};for(let[e,n]of gd)t[e]={...n};return t}async function Eb(t,{method:e="merge",onMerged:n=null}={}){let r=await Xe(t);if(!_s.has(e))throw new Error(`invalid merge method: ${e}`);if(gd.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await As(r.path)).filter(Sd).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 gd.set(r.path,s),uO(s,o,{listPrs:()=>As(r.path),merge:a=>ao(r.path,a,{method:e}),sleep:iO,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function lO(t,e){let n=Date.now()+(e.settleMaxMs??aO),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(sO)}}async function uO(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 lO(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: ${Oe(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(!Sd(a)){t.skipped.push({number:a.number,title:a.title,reason:cO(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:Oe(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Ce()}}async function vd(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await oo("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Oe(l)}`)}await X$(t);let o=n||"";if((await we({cwd:t})).GH_TOKEN){let l=await Ic().catch(()=>null);l&&(o=`${o?`${o}
153
+
154
+ `:""}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 oe(t,s)}catch(l){throw new Error(Oe(l))}finally{Ce()}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 Ib(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await oe(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Oe(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!kd(i))throw i;let c=await oe(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await kb(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Oe(i));o=xb(l).map(yb).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=dO(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await oe(n.path,["run","view",c,"--log-failed"]).then(u=>fO(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function dO(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function fO(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
154
155
  `);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
155
156
  `+o.slice(-e).join(`
156
157
  `)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
157
- `+r.slice(-n)),r}function Ie(t){return String(t?.stderr||t?.message||t||"").trim().split(`
158
- `).find(n=>n.trim())||"gh failed"}var W$=6e3,wd=new Map,q$=30*6e4;function gd(t,e=Date.now()){let n=null;for(let r of t||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&e-n.ts>q$?null:n}async function hb(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await B(t)}catch{return}if(gd(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await vi(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(wd.get(t.id)||0)<W$)return;wd.set(t.id,o);let s;try{s=await xs(t.cwd,{caller:"reconcile"})}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await et(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Jt({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 cs(i,a.number))){await $(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"pushed",number:a.number,url:a.url,title:a.title,branch:e,repo:i}});return}}await $(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,...i?{repo:i}:{}}}).catch(()=>{}),wd.delete(t.id)}import K$ from"node:fs/promises";import kb from"node:path";import G$ from"node:os";import{randomUUID as xb}from"node:crypto";var V$=process.env.BOSTRAT_STATE_DIR||kb.join(process.env.HOME||G$.tmpdir(),".bostrat"),vb=kb.join(V$,"flows.json"),wb=20,gb=80,yb=500,bb=80,Ye=null,yd=null;async function Ze(){return Ye||(yd||=(async()=>{let t=!0;try{Ye=JSON.parse(await K$.readFile(vb,"utf8"))}catch{Ye={flows:[]},t=!1}finally{yd=null}if(Ye.flows||=[],!t&&ht()){let{records:e,source:n}=await $t("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&Ye.flows.push(r.data);Ye.flows.length&&await so().catch(()=>{})}}return Ye})(),yd)}async function so(){await Ft(vb,Ye)}var Mi=t=>Pt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function bd(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Sb({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>wb)throw new Error(`steps must be an array of 1-${wb}`);let r=await Xn({trackId:t}),o=[...n],s=[];for(let i=0;i<e.length;i++){let c=e[i]||{},l=`step ${i+1}`,u=typeof c.caption=="string"?c.caption.trim():"";if(!u)throw new Error(`${l}: caption (string) required`);if(u.length>yb)throw new Error(`${l}: caption over ${yb} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>bb)throw new Error(`${l}: title over ${bb} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(v=>v.id===d);if(p.length||(p=r.filter(v=>v.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(v=>`mock:${v.id}`).join(", ")}) \u2014 use the id`);let m=p[0],h=null;if(c.v!=null&&(h=Number(c.v),!Number.isInteger(h)||!(m.versions||[]).some(v=>v.v===h)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(v=>v.mockId===m.id),k=y>=0?o.splice(y,1)[0].id:xb().slice(0,8);s.push({id:k,mockId:m.id,caption:u,title:f,v:h})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function z$(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>gb)throw new Error(`name over ${gb} chars`);return t.trim()}async function Tb({trackId:t}={}){let e=await Ze();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function J$(t){let n=(await Ze()).flows.find(r=>r.id===t);return n?{...n}:null}async function Ab(t){let e=await J$(t);if(!e)return null;let n=new Map((await Xn({trackId:e.trackId})).map(r=>[r.id,r]));return e.steps=e.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=ae(o,r.v);return{...r,missing:!1,mockName:o.name,mockState:o.state,servedV:s?.v??null,width:s?.width??null,height:s?.height??null}}),e}async function _b({trackId:t,name:e,steps:n,cwd:r,box:o,agent:s}){if(!t)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=z$(e),i=await Ze(),c=i.flows.find(p=>p.trackId===t&&p.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await Sb({trackId:t,steps:n}),f=Date.now(),d={id:xb().slice(0,8),trackId:t,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await so(),await bd(d,"created"),Mi(d),{...d}}async function Eb(t,{steps:e,note:n}={}){let o=(await Ze()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Sb({trackId:o.trackId,steps:e,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await so(),await bd(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Mi(o),{...o}}async function Ib(t){let e=await Ze(),n=e.flows.findIndex(o=>o.id===t);if(n===-1)throw new Error("flow not found");let r=e.flows[n];return e.flows.splice(n,1),await so(),await bd(r,"deleted"),oe("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Rb(t){return(await Ze()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function $b(t){let n=(await Ze()).flows.filter(r=>r.trackId===t.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=t.name||t.id,r.updatedAt=Date.now(),Mi(r);return n.length&&await so(),n.length}async function Ob(t){let e=await Ze(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await so();for(let r of n)oe("flows",r.id).catch(()=>{})}return n.length}async function Mb(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Ze();for(let r of n.flows)await Mi(r);return{pushed:n.flows.length,source:"plane"}}import kd from"node:fs/promises";import Ts from"node:path";import{randomUUID as X$}from"node:crypto";var Nb=".bostrat/uploads",xd=t=>Ts.join(t,Nb),Cb=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,Y$=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Z$=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 vd(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return Y$.has(e)?"image":Z$.has(e)?"doc":null}function Q$(t){let r=Ts.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 Pb(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>Cb)throw new Error(`file too large \u2014 max ${Math.floor(Cb/(1024*1024))} MB`);let r=Q$(e),o=Ts.extname(r).slice(1),s=vd(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=X$().slice(0,8),i=xd(t);await kd.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await kd.writeFile(Ts.join(i,c),n,{mode:384}),await kd.chmod(Ts.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${Nb}/${c}`,kind:s,size:n.length}}function Db(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var tO=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Sd(t){return t.role==="user"&&tO.test((t.text||"").trim())}function pr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var rO=nO(eO),Qe=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),qb=U.join(Qe,"tracks.json"),oO=U.join(Qe,"events.jsonl"),$d=U.join(Qe,"workspaces"),sO=process.env.BOSTRAT_DEFAULT_REPO||null,Di={claude:{envVar:"BOSTRAT_AGENT_CMD",base:"claude",skipFlag:"--dangerously-skip-permissions"},cursor:{envVar:"BOSTRAT_CURSOR_CMD",base:"cursor-agent",skipFlag:"--force"},codex:{envVar:"BOSTRAT_CODEX_CMD",base:"codex",skipFlag:"--dangerously-bypass-approvals-and-sandbox"}},Bi=Object.keys(Di);function aO(t,{skipPermissions:e=!0}={}){let n=Di[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 Kb(t,e,{skipPermissions:n=!0}={}){let r=Di[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 Cs(t){let e=Di[t];if(e)return process.env[e.envVar]||e.base}var On={at:0,value:null};async function Cd({maxAgeMs:t=6e4}={}){if(On.value&&Date.now()-On.at<t)return On.value;let e=On.value,n=await Promise.all(Bi.map(async r=>{let o=await wa(Cs(r),{retries:[]});return o===Ar?[r,e?.[r]??!0]:[r,o===ha]}));return On.value=Object.fromEntries(n),On.at=Date.now(),On.value}async function ji(t,e=!0){let n=aO(t,{skipPermissions:e});if(await wa(n)===Tr){On.at=0;let r=n.split(/\s+/)[0],o=process.env.FLY_APP_NAME?"This Fly node's image predates CLI parity \u2014 update the node image (Links page \u2192 Fly card \u2192 Update) or re-provision.":Wc[r]?`Install it on this node: ${Wc[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). ${o}`)}return n}var Li=".bostrat/handoff/latest.md",iO=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;function cO(t,e=Li){return`${t.trim()}
158
+ `+r.slice(-n)),r}function Oe(t){return String(t?.stderr||t?.message||t||"").trim().split(`
159
+ `).find(n=>n.trim())||"gh failed"}var pO=6e3,Td=new Map,mO=30*6e4;function Ad(t,e=Date.now()){let n=null;for(let r of t||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&e-n.ts>mO?null:n}async function Rb(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await P(t)}catch{return}if(Ad(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await _i(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Td.get(t.id)||0)<pO)return;Td.set(t.id,o);let s;try{s=await As(t.cwd,{caller:"reconcile"})}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await rt(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Xt({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 fs(i,a.number))){await I(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"pushed",number:a.number,url:a.url,title:a.title,branch:e,repo:i}});return}}await I(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,...i?{repo:i}:{}}}).catch(()=>{}),Td.delete(t.id)}import hO from"node:fs/promises";import Nb from"node:path";import wO from"node:os";import{randomUUID as Pb}from"node:crypto";var gO=process.env.BOSTRAT_STATE_DIR||Nb.join(process.env.HOME||wO.tmpdir(),".bostrat"),Db=Nb.join(gO,"flows.json"),$b=20,Ob=80,Cb=500,Mb=80,Ze=null,_d=null;async function Qe(){return Ze||(_d||=(async()=>{let t=!0;try{Ze=JSON.parse(await hO.readFile(Db,"utf8"))}catch{Ze={flows:[]},t=!1}finally{_d=null}if(Ze.flows||=[],!t&&mt()){let{records:e,source:n}=await It("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&Ze.flows.push(r.data);Ze.flows.length&&await io().catch(()=>{})}}return Ze})(),_d)}async function io(){await Ht(Db,Ze)}var Di=t=>Bt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function Ed(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Bb({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>$b)throw new Error(`steps must be an array of 1-${$b}`);let r=await Xn({trackId:t}),o=[...n],s=[];for(let i=0;i<e.length;i++){let c=e[i]||{},l=`step ${i+1}`,u=typeof c.caption=="string"?c.caption.trim():"";if(!u)throw new Error(`${l}: caption (string) required`);if(u.length>Cb)throw new Error(`${l}: caption over ${Cb} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Mb)throw new Error(`${l}: title over ${Mb} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===d);if(p.length||(p=r.filter(S=>S.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let m=p[0],h=null;if(c.v!=null&&(h=Number(c.v),!Number.isInteger(h)||!(m.versions||[]).some(S=>S.v===h)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let g=o.findIndex(S=>S.mockId===m.id),k=g>=0?o.splice(g,1)[0].id:Pb().slice(0,8);s.push({id:k,mockId:m.id,caption:u,title:f,v:h})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function yO(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Ob)throw new Error(`name over ${Ob} chars`);return t.trim()}async function jb({trackId:t}={}){let e=await Qe();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function bO(t){let n=(await Qe()).flows.find(r=>r.id===t);return n?{...n}:null}async function Lb(t){let e=await bO(t);if(!e)return null;let n=new Map((await Xn({trackId:e.trackId})).map(r=>[r.id,r]));return e.steps=e.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=ce(o,r.v);return{...r,missing:!1,mockName:o.name,mockState:o.state,servedV:s?.v??null,width:s?.width??null,height:s?.height??null}}),e}async function Fb({trackId:t,name:e,steps:n,cwd:r,box:o,agent:s}){if(!t)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=yO(e),i=await Qe(),c=i.flows.find(p=>p.trackId===t&&p.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await Bb({trackId:t,steps:n}),f=Date.now(),d={id:Pb().slice(0,8),trackId:t,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await io(),await Ed(d,"created"),Di(d),{...d}}async function Ub(t,{steps:e,note:n}={}){let o=(await Qe()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Bb({trackId:o.trackId,steps:e,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await io(),await Ed(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Di(o),{...o}}async function Hb(t){let e=await Qe(),n=e.flows.findIndex(o=>o.id===t);if(n===-1)throw new Error("flow not found");let r=e.flows[n];return e.flows.splice(n,1),await io(),await Ed(r,"deleted"),se("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Wb(t){return(await Qe()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function qb(t){let n=(await Qe()).flows.filter(r=>r.trackId===t.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=t.name||t.id,r.updatedAt=Date.now(),Di(r);return n.length&&await io(),n.length}async function Kb(t){let e=await Qe(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await io();for(let r of n)se("flows",r.id).catch(()=>{})}return n.length}async function Gb(){if(!mt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await It("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Qe();for(let r of n.flows)await Di(r);return{pushed:n.flows.length,source:"plane"}}import Id from"node:fs/promises";import Is from"node:path";import{randomUUID as kO}from"node:crypto";var zb=".bostrat/uploads",Rd=t=>Is.join(t,zb),Vb=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,xO=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),SO=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 $d(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return xO.has(e)?"image":SO.has(e)?"doc":null}function vO(t){let r=Is.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 Jb(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>Vb)throw new Error(`file too large \u2014 max ${Math.floor(Vb/(1024*1024))} MB`);let r=vO(e),o=Is.extname(r).slice(1),s=$d(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=kO().slice(0,8),i=Rd(t);await Id.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Id.writeFile(Is.join(i,c),n,{mode:384}),await Id.chmod(Is.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${zb}/${c}`,kind:s,size:n.length}}function Yb(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var TO=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Od(t){return t.role==="user"&&TO.test((t.text||"").trim())}function mr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var EO=_O(AO),tn=process.env.BOSTRAT_STATE_DIR||H.join(process.env.HOME||"/tmp",".bostrat"),ok=H.join(tn,"tracks.json"),IO=H.join(tn,"events.jsonl"),jd=H.join(tn,"workspaces"),RO=process.env.BOSTRAT_DEFAULT_REPO||null,Fi={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"}},Ui=Object.keys(Fi);function $O(t,{skipPermissions:e=!0}={}){let n=Fi[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 sk(t,e,{skipPermissions:n=!0}={}){let r=Fi[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 js(t){let e=Fi[t];if(e)return process.env[e.envVar]||e.base}var Cn={at:0,value:null};async function Fd({maxAgeMs:t=6e4}={}){if(Cn.value&&Date.now()-Cn.at<t)return Cn.value;let e=Cn.value,n=await Promise.all(Ui.map(async r=>{let o=await xa(js(r),{retries:[]});return o===_r?[r,e?.[r]??!0]:[r,o===ka]}));return Cn.value=Object.fromEntries(n),Cn.at=Date.now(),Cn.value}async function Hi(t,e=!0){let n=$O(t,{skipPermissions:e});if(await xa(n)===Ar){Cn.at=0;let r=n.split(/\s+/)[0],o=process.env.FLY_APP_NAME?"This Fly node's image predates CLI parity \u2014 update the node image (Links page \u2192 Fly card \u2192 Update) or re-provision.":zc[r]?`Install it on this node: ${zc[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). ${o}`)}return n}var Wi=".bostrat/handoff/latest.md",OO=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;function CO(t,e=Wi){return`${t.trim()}
159
160
 
160
- (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 lO=/\(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 Bb(t){return t.role==="user"&&lO.test((t.text||"").trim())}var Ci=".bostrat/handoff/subtask-result.md",_s=".bostrat/handoff/lead-context.md",uO=(t,e=!1)=>e?`You are a SUBTASK agent working in an isolated multi-repo workspace, delegated by a lead agent on a shared workstream. This directory is NOT itself a git repo: each subdirectory is an isolated git worktree of one of the workstream's repos, already seeded with the lead's full working tree for that repo. The conversation so far is summarized in ${_s} \u2014 read that first for context. Your scoped task:
161
+ (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 MO=/\(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 Xb(t){return t.role==="user"&&MO.test((t.text||"").trim())}var Bi=".bostrat/handoff/subtask-result.md",$s=".bostrat/handoff/lead-context.md",NO=(t,e=!1)=>e?`You are a SUBTASK agent working in an isolated multi-repo workspace, delegated by a lead agent on a shared workstream. This directory is NOT itself a git repo: each subdirectory is an isolated git worktree of one of the workstream's repos, already seeded with the lead's full working tree for that repo. The conversation so far is summarized in ${$s} \u2014 read that first for context. Your scoped task:
161
162
 
162
163
  ${t}
163
164
 
164
- Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${Ci} (at this directory's root) with sections \u2014 Result (what you did), Files (paths you changed, prefixed with the repo directory), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`:`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 ${_s} \u2014 read that first for context. Your scoped task:
165
+ Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${Bi} (at this directory's root) with sections \u2014 Result (what you did), Files (paths you changed, prefixed with the repo directory), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`:`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 ${$s} \u2014 read that first for context. Your scoped task:
165
166
 
166
167
  ${t}
167
168
 
168
- 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 ${Ci} 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.`,dO=(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."),$e=null,Td=null;async function H(){return $e||(Td||=(async()=>{try{$e=JSON.parse(await F.readFile(qb,"utf8"))}catch{$e={tracks:[]}}finally{Td=null}return $e})(),Td)}async function V(){await Ft(qb,$e)}var Z=(t,e)=>t.tracks.find(n=>n.id===e)||null,Ad=Promise.resolve();function fO(t,e){let n=JSON.stringify({trackId:t,...e})+`
169
- `;return Ad=Ad.then(()=>F.mkdir(Qe,{recursive:!0})).then(()=>F.appendFile(oO,n)).catch(()=>{}),Ad}var dt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),fO(t.id,r),ly(t,r),r};Lp(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&&(dt(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await V().catch(()=>{}))});var _d=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),pO=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function Gb(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=>pO.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 Vb(t,e,n,{kind:r}={}){let o=await tt(e);if(!o)throw new Error(`box "${e}" is not running`);let s=Cs(o.agent);await pn({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await Et(e,n,{...Pe(o.agent),kind:r||"orchestration"}),await q({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await H(),i=Z(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);dt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await V()}var mr=new Set(["merged","conflict","canceled","failed","handed-off"]),jb=new Map;async function mO(t){let e=U.join(Ra(t),`${t.id}.ndjson`),n;try{n=await F.stat(e)}catch{return null}let r=jb.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 jb.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function hO(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Xu(n.name,n.agent)}async function tn(t,e=null){let n=e??new Map((await Ce()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=mr.has(i.state)?i.state:c?i.role==="lead"?"live":i.state:"stopped";return{...i,state:l,cwd:c?.cwd||i.cwd,agent:c?.agent||i.agent,sessionId:c?.sessionId||i.sessionId}}),[o,{working:s,prompted:a}]=await Promise.all([mO(t),hO(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Ni.has(t.id)}}var wO=5e3;function gO(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),f=l.createdAt-wO,d=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=f&&m.birthMs<d);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function zb(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(d=>d.box.sessionId),a=Cp(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=gO(u,a,d=>i.has(d));for(let d of f){let p=u.find(m=>m.name===d.box);p&&(p.sessionId=d.sessionId),await Tp(d.box,d.sessionId),n=!0}}return n}async function st(){let t=await H();await zb(t)&&await V();let e=new Map((await Ce()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>tn(n,e)))}async function Ai(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 Jb(t){let e=Ao(t);if(!e)return null;let n=await H();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Rf(o.hookTokenHash,e))return{track:r,box:o};return null}async function Q(t){let e=await H(),n=Z(e,t);return n?(await zb(e)&&await V(),tn(n)):null}var yO={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function Lb(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(yO[t.agent]||[]).some(n=>n.test(e))}var bO=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function Fb(t){let e=(t.text||"").trim();return e?bO.some(n=>n.test(e)):!1}var kO=10*6e4;async function Nd(t,e={}){let n=await Q(t);if(!n)return null;let r=new Set([...n.events.filter(b=>b.type==="orchestration-prompt"&&b.prompt).map(b=>String(b.prompt).trim()),...n.events.filter(b=>b.type==="said"&&b.text).map(b=>String(b.text).trim())]),o=Ns(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=Fl(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Gh(n),...l],d={windows:[...eh(n),...nh(n,Pa),...a,...u]},m=(await Promise.all(n.boxes.map(b=>_u(b,e)))).flat(),h=m.map(b=>{let _={kind:"message",...b};if(_.role==="assistant"&&(_.text||"").trim()&&_.tools?.length){let lt=ps(_.agent).cliTools,nt=_.tools.filter(bt=>!lt.has(bt));return{..._,tools:nt.length?nt:void 0}}return _}).map(b=>{if(b.role!=="user")return b;let _=Ma(b.text);return _?_.kind==="suppress"?null:{...b,note:_,text:_m(_)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!Lb(b)).filter(b=>!Fb(b)).filter(b=>!Bb(b)).filter(b=>!Sd(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!rh(b,d)),y=h.filter(b=>b.failed&&b.text===hl);if(y.length){let b=new Map;for(let _ of y){if(!b.has(_.box)){let bt=[...(await mt(_.box,{lines:_t}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(jt=>jt[1]);b.set(_.box,bt.length?bt[bt.length-1]:null)}let lt=b.get(_.box);lt&&(_.text=`\u25A0 ${lt}`)}}let k=await Promise.all(n.boxes.map(b=>kg(b,e)));await Vy(n,k.flat()),await Ky(n,h),await uy(n),await Qh(n,h).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await zy(n,b),b.agent==="cursor"&&b.state==="live"&&await Zy(n,b);let v=await Promise.all(n.boxes.map(b=>bg(b,e)));await Gy(n,v.flat().filter(b=>!_d(b.box,b.ts,u)));let I=await B(n),R=c&&i.viewId?i.viewId:null,z=ky(I),O=zn(I).filter(b=>!(b.kind==="message"&&(Lb(b)||Fb(b)||Bb(b)||Sd(b)))&&!(b.kind==="view-card"&&R&&b.viewId===R)),M=xy(O,z),S=O.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!M.has(b)&&_d(b.box,b.ts,z))),N=Al(I,S).map(b=>({kind:"command-batch",id:b.id,ts:b.pending?b.ts:(b.anchorTs??b.ts)-1,anchorTs:b.anchorTs,anchorBox:b.anchorBox,agent:b.agent,boxRole:b.boxRole,commands:b.commands,pending:b.pending})).filter(b=>!_d(b.anchorBox,b.anchorTs??b.ts,u)),D=Cm(Nm([...S,...N].filter(b=>b.ts!=null).sort((b,_)=>b.ts-_.ts))),it=xO(n),ct=it.size?D.filter(b=>b.kind==="event"||!it.has(b.box??b.anchorBox)):D,T=n.boxes.find(b=>b.name===n.leadBox)||null,g=T?await jo(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!g&&T){let b=[...I].reverse().find(_=>_.type==="context.usage"&&_.boxId===T.name);b?.payload?.window&&(g={pct:b.payload.pct??(b.payload.occupancy!=null?b.payload.occupancy/b.payload.window:null),occupancy:b.payload.occupancy??null,window:b.payload.window,windowKnown:!0,model:b.payload.model||null})}let w=[...m].reverse().find(b=>b.role==="assistant"&&b.model),A=w?.model||null,E=[...n.events||[]].reverse().find(b=>b.type==="model-change"),J=g?.model||A;E&&(!w?.ts||E.at>=w.ts)&&(J=E.to);let W=T?await mt(T.name,{lines:_t}).catch(()=>""):"",at=T&&Vr(T.agent,W)?await dn(T.name).catch(()=>W):W,Nt=T?lr(T,at):null;!Nt&&T&&!Fo(T.agent,W)&&!Od(n.id).length&&(Nt=Tl(ct,{leadBoxId:T.name,provider:T.agent,isAnswered:b=>I.some(_=>_.type==="prompt.answered"&&_.payload?.promptId===b)})),Nt&&dy(n,T,Nt);let Tt=ct.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),gr=!!T&&xi(T.name,T.agent,W)&&!Nt;T&&!T.readyAt&&(gr||Nt||W&&Ta(T.agent,W))&&(T.readyAt=Date.now(),await V());let Ys=!!T&&!T.readyAt&&Date.now()-(n.createdAt||0)<kO;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&Ta(T.agent,W)&&!Nt){let b=await H(),_=Z(b,n.id);_?.setup&&!_.setup.connectedAt&&(_.setup.connectedAt=Date.now(),n.setup.connectedAt=_.setup.connectedAt,await $(_,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:_.setup.connectedAt}}).catch(()=>{}),await V())}let G=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,j=null;if(c){let b=Date.now()-i.startedAt;if(i.viewId&&!gr){let _=Ua(n.id);_&&(dt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:_.finishedAt}),await V())}else if(Nt||b>qh&&!gr&&!i.viewId||b>Ll){let _=Nt?"needs-input":b>Ll?"timeout":"no-view";Ua(n.id,{error:_})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:_}}).catch(()=>{})}else j={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Tt,context:g?{pct:g.pct,occupancy:g.occupancy,window:g.window,windowKnown:g.windowKnown,model:g.model}:null,models:{current:J,options:oy(T?.agent)},pendingPrompt:Nt,working:gr,stopping:$O(n.id),leadStarting:Ys,handoff:G,view:j,queued:Od(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function xO(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 Xb(t,e){let n=await Q(t);if(!n)return null;let r=ho(t).find(f=>f.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=zn(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Al(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await mt(o,{lines:_t}).catch(()=>"");u=xi(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Pd(t,{promptId:e,answers:n,text:r}={}){let o=Z(await H(),t);if(!o)throw new Error(`track "${t}" not found`);Ui(t,"prompt-answer");let s=o.boxes.find(d=>d.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await tt(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Cu(s);if(i||(i=await vO(o,s)),!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 d=typeof r=="string"?r.trim():"";if(!d)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(d)||d.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Et(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await At(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=$m(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await ue(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=Og(i,n||[]);await At(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=Gp(i,n||[]);await Et(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:d,count:1}}if(i.answerMode==="say"){let d=i.questions||[];for(let p=0;p<d.length;p++){let m=d[p],h=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));zu(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:p,questionTotal:i.questionTotal??d.length,header:m.header||"",question:m.question||"",multiSelect:!!m.multiSelect,chosen:h})}return await q({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(d=>l.options?.[d]).filter(Boolean).map(d=>({label:d.label,description:d.description||""})),f=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,...f?{control:f}:{}},await q({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),zu(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function vO(t,e){if(!e)return null;let n=await B(t).catch(()=>[]);return Tl(zn(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Yb(t,{promptId:e,questionIndex:n}={}){let r=Z(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 tt(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Cu(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=Mg(a,n);return c.length?(await At(r.leadBox,c,{kind:"navigate-prompt"}),await q({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 SO=1500,Es=new Map;function Dd(t){let e=Es.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},Es.set(t,e)),e}function Od(t){return(Es.get(t)?.items??[]).map(e=>({...e}))}function Fi(t){let e=Es.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}async function Zb(t){let e=Fi(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=Z(await H(),t);if(o)return r=uo(o,n),await V(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function Qb(t){let e=Dd(t);e.timer||(e.timer=setInterval(()=>TO(t).catch(()=>{}),SO))}async function TO(t){let e=Es.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=Z(await H(),t);if(!n||n.archivedAt){Fi(t);return}let r=Ns(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){pr(e,!1);return}let o=await tt(n.leadBox);if(!o){pr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(Zr(s?.agent)){pr(e,!1);return}let a=await mt(n.leadBox,{lines:_t}).catch(()=>"");if(Sa(s?.agent,a)){pr(e,!1);return}if(Vr(s?.agent,a)){let c=await dn(n.leadBox).catch(()=>a);if(lr(s,c)){pr(e,!1);return}}if(!pr(e,!0))return;let i=e.items.shift();e.idleSince=null,await ek(n,s,o,i.text,i.kind??null,i.attachments??null),await V()}finally{e.flushing=!1}}}async function tk(t,e,n,r){let o=Zr(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(uo(t,[n]),await V()),zg(t.id,o))try{await $(t,{type:"usage.cap.hit",actor:"system",provider:e?.agent??null,boxId:t.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${e?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function ek(t,e,n,r,o=null,s=null){await tk(t,e,r,o);let a=Is.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=cO(r,t.pendingHandoffHydrate.artifact||Li),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=Db(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
169
+ 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 ${Bi} 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.`,PO=(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."),Me=null,Cd=null;async function W(){return Me||(Cd||=(async()=>{try{Me=JSON.parse(await U.readFile(ok,"utf8"))}catch{Me={tracks:[]}}finally{Cd=null}return Me})(),Cd)}async function z(){await Ht(ok,Me)}var tt=(t,e)=>t.tracks.find(n=>n.id===e)||null,Md=Promise.resolve();function DO(t,e){let n=JSON.stringify({trackId:t,...e})+`
170
+ `;return Md=Md.then(()=>U.mkdir(tn,{recursive:!0})).then(()=>U.appendFile(IO,n)).catch(()=>{}),Md}var dt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),DO(t.id,r),vy(t,r),r};zp(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await W()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(dt(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await z().catch(()=>{}))});var Nd=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),BO=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function ak(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=>BO.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 ik(t,e,n,{kind:r}={}){let o=await et(e);if(!o)throw new Error(`box "${e}" is not running`);let s=js(o.agent);await mn({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await $t(e,n,{...Be(o.agent),kind:r||"orchestration"}),await K({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await W(),i=tt(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);dt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await z()}var hr=new Set(["merged","conflict","canceled","failed","handed-off"]),Zb=new Map;async function jO(t){let e=H.join(Na(t),`${t.id}.ndjson`),n;try{n=await U.stat(e)}catch{return null}let r=Zb.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await P(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return Zb.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function LO(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:od(n.name,n.agent)}async function en(t,e=null){let n=e??new Map((await ae()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=hr.has(i.state)?i.state:c?i.role==="lead"?"live":i.state:"stopped";return{...i,state:l,cwd:c?.cwd||i.cwd,agent:c?.agent||i.agent,sessionId:c?.sessionId||i.sessionId}}),[o,{working:s,prompted:a}]=await Promise.all([jO(t),LO(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:ji.has(t.id)}}var FO=5e3;function UO(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),f=l.createdAt-FO,d=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=f&&m.birthMs<d);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function ck(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(d=>d.box.sessionId),a=Hp(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=UO(u,a,d=>i.has(d));for(let d of f){let p=u.find(m=>m.name===d.box);p&&(p.sessionId=d.sessionId),await Mp(d.box,d.sessionId),n=!0}}return n}async function nt(){let t=await W();await ck(t)&&await z();let e=new Map((await ae()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>en(n,e)))}async function Ri(t,e){let n=await W();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 lk(t){let e=Eo(t);if(!e)return null;let n=await W();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&jf(o.hookTokenHash,e))return{track:r,box:o};return null}async function Z(t){let e=await W(),n=tt(e,t);return n?(await ck(e)&&await z(),en(n)):null}var HO={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function Qb(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(HO[t.agent]||[]).some(n=>n.test(e))}var WO=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function tk(t){let e=(t.text||"").trim();return e?WO.some(n=>n.test(e)):!1}var qO=10*6e4;async function Ud(t,e={}){let n=await Z(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=Ls(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=Gl(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...tw(n),...l],d={windows:[...uh(n),...dh(n,Fa),...a,...u]},m=(await Promise.all(n.boxes.map(y=>Mu(y,e)))).flat(),h=m.map(y=>{let _={kind:"message",...y};if(_.role==="assistant"&&(_.text||"").trim()&&_.tools?.length){let lt=gs(_.agent).cliTools,st=_.tools.filter(xt=>!lt.has(xt));return{..._,tools:st.length?st:void 0}}return _}).map(y=>{if(y.role!=="user")return y;let _=Ba(y.text);return _?_.kind==="suppress"?null:{...y,note:_,text:Pm(_)}:y}).filter(Boolean).filter(y=>!(y.role==="user"&&r.has((y.text||"").trim()))).filter(y=>!Qb(y)).filter(y=>!tk(y)).filter(y=>!Xb(y)).filter(y=>!Od(y)).filter(y=>y.role!=="assistant"||!!(y.text||"").trim()||!!y.images?.length).filter(y=>!fh(y,d)),g=h.filter(y=>y.failed&&y.text===kl);if(g.length){let y=new Map;for(let _ of g){if(!y.has(_.box)){let xt=[...(await wt(_.box,{lines:Rt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Ft=>Ft[1]);y.set(_.box,xt.length?xt[xt.length-1]:null)}let lt=y.get(_.box);lt&&(_.text=`\u25A0 ${lt}`)}}let k=await Promise.all(n.boxes.map(y=>$g(y,e)));await ib(n,k.flat()),await sb(n,h),await Ty(n),await iw(n,h).catch(()=>{});for(let y of n.boxes)y.agent==="codex"&&y.state==="live"&&await cb(n,y),y.agent==="cursor"&&y.state==="live"&&await fb(n,y);let S=await Promise.all(n.boxes.map(y=>Rg(y,e)));await ab(n,S.flat().filter(y=>!Nd(y.box,y.ts,u)));let R=await P(n),$=c&&i.viewId?i.viewId:null,J=Ny(R),O=Jn(R).filter(y=>!(y.kind==="message"&&(Qb(y)||tk(y)||Xb(y)||Od(y)))&&!(y.kind==="view-card"&&$&&y.viewId===$)),N=Py(O,J),A=O.filter(y=>!(y.kind==="message"&&y.role==="assistant"&&!N.has(y)&&Nd(y.box,y.ts,J))),C=$l(R,A).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=>!Nd(y.anchorBox,y.anchorTs??y.ts,u)),F=Hm(Wm([...A,...C].filter(y=>y.ts!=null).sort((y,_)=>y.ts-_.ts))),ct=KO(n),Y=ct.size?F.filter(y=>y.kind==="event"||!ct.has(y.box??y.anchorBox)):F,T=n.boxes.find(y=>y.name===n.leadBox)||null,L=T?await Uo(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!L&&T){let y=[...R].reverse().find(_=>_.type==="context.usage"&&_.boxId===T.name);y?.payload?.window&&(L={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 _t=[...m].reverse().find(y=>y.role==="assistant"&&y.model),b=_t?.model||null,w=[...n.events||[]].reverse().find(y=>y.type==="model-change"),v=L?.model||b;w&&(!_t?.ts||w.at>=_t.ts)&&(v=w.to);let E=T?await wt(T.name,{lines:Rt}).catch(()=>""):"",pt=T&&Jr(T.agent,E)?await fn(T.name).catch(()=>E):E,q=T?ur(T,pt):null;if(q&&T){let y=fi(T,q,pt);if(y){let _=Kg(T.name,q.promptId);_.action!=="give-up"&&(_.action==="send"&&(kt(T.name,y.keys,{kind:"auto-reject"}).catch(()=>{}),_.first&&I(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{ts:Date.now(),promptId:q.promptId,rule:y.id}}).catch(()=>{})),q=null)}}!q&&T&&!Wo(T.agent,E)&&!Cs(n.id).length&&(q=Rl(Y,{leadBoxId:T.name,provider:T.agent,isAnswered:y=>R.some(_=>_.type==="prompt.answered"&&_.payload?.promptId===y)})),q&&Ay(n,T,q);let at=Y.filter(y=>!(y.kind==="message"&&y.role==="assistant"&&y.ask&&!String(y.text||"").trim()&&!y.images?.length)),Et=!!T&&Ai(T.name,T.agent,E)&&!q;T&&!T.readyAt&&(Et||q||E&&Ra(T.agent,E))&&(T.readyAt=Date.now(),await z());let yr=!!T&&!T.readyAt&&Date.now()-(n.createdAt||0)<qO;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&Ra(T.agent,E)&&!q){let y=await W(),_=tt(y,n.id);_?.setup&&!_.setup.connectedAt&&(_.setup.connectedAt=Date.now(),n.setup.connectedAt=_.setup.connectedAt,await I(_,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:_.setup.connectedAt}}).catch(()=>{}),await z())}let V=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,B=null;if(c){let y=Date.now()-i.startedAt;if(i.viewId&&!Et){let _=Ga(n.id);_&&(dt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:_.finishedAt}),await z())}else if(q||y>Zh&&!Et&&!i.viewId||y>Kl){let _=q?"needs-input":y>Kl?"timeout":"no-view";Ga(n.id,{error:_})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:_}}).catch(()=>{})}else B={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:at,context:L?{pct:L.pct,occupancy:L.occupancy,window:L.window,windowKnown:L.windowKnown,model:L.model}:null,models:{current:v,options:yy(T?.agent)},pendingPrompt:q,working:Et,stopping:tC(n.id),leadStarting:yr,handoff:V,view:B,queued:Cs(n.id).filter(y=>y.kind!=="ship-notify"&&y.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function KO(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 uk(t,e){let n=await Z(t);if(!n)return null;let r=wo(t).find(f=>f.id===e)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await P(n),a=Jn(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=$l(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await wt(o,{lines:Rt}).catch(()=>"");u=Ai(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Hd(t,{promptId:e,answers:n,text:r}={}){let o=tt(await W(),t);if(!o)throw new Error(`track "${t}" not found`);Ki(t,"prompt-answer");let s=o.boxes.find(d=>d.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await et(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Fu(s);if(i||(i=await GO(o,s)),!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 d=typeof r=="string"?r.trim():"";if(!d)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(d)||d.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await $t(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await kt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=Lm(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await fe(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=Ug(i,n||[]);await kt(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=em(i,n||[]);await $t(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:d,count:1}}if(i.answerMode==="say"){let d=i.questions||[];for(let p=0;p<d.length;p++){let m=d[p],h=(Array.isArray(n?.[p])?n[p]:[]).map(g=>m.options?.[g]).filter(Boolean).map(g=>({label:g.label,description:g.description||""}));nd(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:p,questionTotal:i.questionTotal??d.length,header:m.header||"",question:m.question||"",multiSelect:!!m.multiSelect,chosen:h})}return await K({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(d=>l.options?.[d]).filter(Boolean).map(d=>({label:d.label,description:d.description||""})),f=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,...f?{control:f}:{}},await K({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),nd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function GO(t,e){if(!e)return null;let n=await P(t).catch(()=>[]);return Rl(Jn(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function dk(t,{promptId:e,questionIndex:n}={}){let r=tt(await W(),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 et(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Fu(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=Hg(a,n);return c.length?(await kt(r.leadBox,c,{kind:"navigate-prompt"}),await K({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 VO=1500,Os=new Map;function Wd(t){let e=Os.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},Os.set(t,e)),e}function Cs(t){return(Os.get(t)?.items??[]).map(e=>({...e}))}function qi(t){let e=Os.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}async function fk(t){let e=qi(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=tt(await W(),t);if(o)return r=po(o,n),await z(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function pk(t){let e=Wd(t);e.timer||(e.timer=setInterval(()=>zO(t).catch(()=>{}),VO))}async function zO(t){let e=Os.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=tt(await W(),t);if(!n||n.archivedAt){qi(t);return}let r=Ls(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){mr(e,!1);return}let o=await et(n.leadBox);if(!o){mr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(to(s?.agent)){mr(e,!1);return}let a=await wt(n.leadBox,{lines:Rt}).catch(()=>"");if(Ia(s?.agent,a)){mr(e,!1);return}if(Jr(s?.agent,a)){let c=await fn(n.leadBox).catch(()=>a);if(ur(s,c)){mr(e,!1);return}}if(!mr(e,!0))return;let i=e.items.shift();e.idleSince=null,await hk(n,s,o,i.text,i.kind??null,i.attachments??null),await z()}finally{e.flushing=!1}}}async function mk(t,e,n,r){let o=to(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(po(t,[n]),await z()),cy(t.id,o))try{await I(t,{type:"usage.cap.hit",actor:"system",provider:e?.agent??null,boxId:t.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${e?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function hk(t,e,n,r,o=null,s=null){await mk(t,e,r,o);let a=Ms.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=CO(r,t.pendingHandoffHydrate.artifact||Wi),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=Yb(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
170
171
 
171
- ${f}`:f}let u=dt(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...l.length?{attachments:l}:{}});lo(t,null);try{if(await pn({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:Cs(n.agent)}),(Is.get(t.id)||0)!==a){!o&&r.trim()&&(uo(t,[r]),await V());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}await Et(t.leadBox,i,{...Pe(e?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){throw dt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,...f?.interrupted?{reason:"interrupted"}:{}}),f}await FO(t,e,i)||(await sk(t.leadBox,e?.agent,i.length+8).catch(()=>{}),dt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,reason:"unsubmitted"}),o||uo(t,[r])),c&&await q({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function nk(t,{actions:e}={}){Dd(t).items.push({id:Ms(),text:vy(e),ts:Date.now(),kind:"ship-notify"}),Qb(t)}async function ue(t,e,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(d=>d&&typeof d.rel=="string"):[];if((typeof e!="string"||!zr(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Z(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=s.boxes.find(d=>d.name===s.leadBox),i=zr(e);await tk(s,a,i,n),Ui(t,"say");let c=await tt(s.leadBox);if(!c&&await Fd(s)&&(await V(),c=await tt(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Dd(t),u=await mt(s.leadBox,{lines:_t}).catch(()=>""),f=l.items.length>0||Sa(a?.agent,u);if(!f&&Vr(a?.agent,u)){let d=await dn(s.leadBox).catch(()=>u);f=!!lr(a,d)}if(f){let d={id:Ms(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),Qb(t),lo(s,null),await V(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await ek(s,a,c,i,n,o),await V(),{ok:!0,leadBox:s.leadBox}}var Ub="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 rk(t,{hint:e}={}){let n=Z(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=Fl(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";Kh(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${Ub}
172
+ ${f}`:f}let u=dt(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...l.length?{attachments:l}:{}});fo(t,null);try{if(await mn({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:js(n.agent)}),(Ms.get(t.id)||0)!==a){!o&&r.trim()&&(po(t,[r]),await z());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}await $t(t.leadBox,i,{...Be(e?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){throw dt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,...f?.interrupted?{reason:"interrupted"}:{}}),f}await uC(t,e,i)||(await bk(t.leadBox,e?.agent,i.length+8).catch(()=>{}),dt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,reason:"unsubmitted"}),o||po(t,[r])),c&&await K({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function wk(t,{actions:e}={}){Wd(t).items.push({id:Bs(),text:Dy(e),ts:Date.now(),kind:"ship-notify"}),pk(t)}async function fe(t,e,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(d=>d&&typeof d.rel=="string"):[];if((typeof e!="string"||!Yr(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=tt(await W(),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=s.boxes.find(d=>d.name===s.leadBox),i=Yr(e);await mk(s,a,i,n),Ki(t,"say");let c=await et(s.leadBox);if(!c&&await Vd(s)&&(await z(),c=await et(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Wd(t),u=await wt(s.leadBox,{lines:Rt}).catch(()=>""),f=l.items.length>0||Ia(a?.agent,u);if(!f&&Jr(a?.agent,u)){let d=await fn(s.leadBox).catch(()=>u);f=!!ur(a,d)}if(f){let d={id:Bs(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),pk(t),fo(s,null),await z(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await hk(s,a,c,i,n,o),await z(),{ok:!0,leadBox:s.leadBox}}var ek="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 gk(t,{hint:e}={}){let n=tt(await W(),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=Gl(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";Qh(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${ek}
172
173
 
173
- What to preview: ${String(e).trim()}`:Ub;try{await ue(t,a,{kind:"spin-up-view"})}catch(i){throw Ua(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Bd(t){let e=Z(await H(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);Is.set(t,(Is.get(t)||0)+1);let n=Fi(t),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=co.get(t);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=uo(e,r),await V()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:e.draftRev||0}}let s=OO(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Mn(AO)]);let a=Z(await H(),t)||e;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}var AO=4e3,_O=3,EO=[300,350,400],IO=1e3,RO=12,co=new Map,Is=new Map;function $O(t){let e=co.get(t);return!!e&&!e.canceled}function Ui(t,e){let n=co.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function ok(t){Ui(t,"track-ended"),Is.delete(t),Fi(t)}function OO(t,{droppedCount:e,droppedTexts:n}){let r={startedAt:Date.now(),clicks:1,escSends:0,bgSends:0,queuedTexts:[...n],canceled:!1,cancelReason:null,result:null,restored:null},o;return r.settled=new Promise(s=>{o=s}),co.set(t,r),(async()=>{let s=Z(await H(),t),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await tt(s.leadBox):null,c=!1,l=!1;if(i){t:for(let y=0;y<_O&&!r.canceled;y++){await At(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of EO){await Mn(k);let v=await mt(s.leadBox,{lines:_t}).catch(()=>"");if(!Fo(a?.agent,v)){c=!0;break t}}}if(!c){l=!0;for(let y=0;y<RO&&!r.canceled&&(await Mn(IO),!r.canceled);y++){let k=await mt(s.leadBox,{lines:_t}).catch(()=>"");if(!Fo(a?.agent,k)){c=!0;break}await At(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=Z(await H(),t)||s,f=u?.boxes.find(y=>y.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await PO(u,f).catch(()=>null),d||(p=await jO(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await CO(u).catch(()=>null):null,h=[d||p,...r.queuedTexts].filter(Boolean);u&&h.length&&(r.restored=uo(u,h),await V()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await MO(u||s,f,{ts:r.startedAt,result:r.result,dropped:e,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await q({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:t,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),co.get(t)===r&&co.delete(t),o()}),r}async function MO(t,e,n){await $(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}async function CO(t){let e=gd(await B(t));return e?.offerId?(await $(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 NO=[250,400,500,600];async function PO(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 NO){await Mn(r);let o=await mt(t.leadBox,{lines:_t}).catch(()=>""),s=Go(e.agent,o);if(s&&wl(n.text,s))return await sk(t.leadBox,e.agent,n.text.length+8),dt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),n.text}return null}var DO=3e4,BO=[350,650,900];async function jO(t,e){if(e?.agent!=="claude")return null;let n=t.events||[],r=[...n].reverse().find(s=>s.type==="said"&&s.text);if(!r||r.kind||Date.now()-r.at>DO||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!vt(r.text))return null;for(let s of BO){await Mn(s);let a;try{a=await _u(e,{})}catch{return null}if(bm(a,r))return null;let i=await mt(t.leadBox,{lines:_t}).catch(()=>"");if(Go(e.agent,i))return null}return dt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}var LO=[250,450,700],Ed=2;async function FO(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=Ed;r++){let o=!1;for(let s of LO){await Mn(s);let a=await mt(t.leadBox,{lines:_t}).catch(()=>""),i=km(e.agent,n,a,{busy:Fo(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Ed&&(await At(t.leadBox,["Enter"],{kind:"submit-retry"}),q({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return q({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:Ed,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function sk(t,e,n){for(let r=0;r<3;r++){await ya(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Mn(200);let o=await mt(t,{lines:_t}).catch(()=>""),s=Go(e,o);if(!s)return!0;n=s.length+8}return!1}function lo(t,e,{source:n}={}){t.draftRev=(t.draftRev||0)+1;let r=typeof e=="string"?e:"";return r.trim()?t.draft={text:r,updatedAt:Date.now(),...n?{source:n}:{}}:delete t.draft,t.draft||null}function uo(t,e){let n=[],r=o=>n.some(s=>vt(s).includes(vt(o)));for(let o of[t.draft?.text,...e])o&&o.trim()&&!r(o)&&n.push(o);return n.length?lo(t,n.join(`
174
+ What to preview: ${String(e).trim()}`:ek;try{await fe(t,a,{kind:"spin-up-view"})}catch(i){throw Ga(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function qd(t){let e=tt(await W(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);Ms.set(t,(Ms.get(t)||0)+1);let n=qi(t),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=uo.get(t);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=po(e,r),await z()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:e.draftRev||0}}let s=eC(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Mn(JO)]);let a=tt(await W(),t)||e;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}var JO=4e3,YO=3,XO=[300,350,400],ZO=1e3,QO=12,uo=new Map,Ms=new Map;function tC(t){let e=uo.get(t);return!!e&&!e.canceled}function Ki(t,e){let n=uo.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function yk(t){Ki(t,"track-ended"),Ms.delete(t),qi(t)}function eC(t,{droppedCount:e,droppedTexts:n}){let r={startedAt:Date.now(),clicks:1,escSends:0,bgSends:0,queuedTexts:[...n],canceled:!1,cancelReason:null,result:null,restored:null},o;return r.settled=new Promise(s=>{o=s}),uo.set(t,r),(async()=>{let s=tt(await W(),t),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await et(s.leadBox):null,c=!1,l=!1;if(i){t:for(let g=0;g<YO&&!r.canceled;g++){await kt(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of XO){await Mn(k);let S=await wt(s.leadBox,{lines:Rt}).catch(()=>"");if(!Wo(a?.agent,S)){c=!0;break t}}}if(!c){l=!0;for(let g=0;g<QO&&!r.canceled&&(await Mn(ZO),!r.canceled);g++){let k=await wt(s.leadBox,{lines:Rt}).catch(()=>"");if(!Wo(a?.agent,k)){c=!0;break}await kt(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=tt(await W(),t)||s,f=u?.boxes.find(g=>g.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await sC(u,f).catch(()=>null),d||(p=await cC(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await rC(u).catch(()=>null):null,h=[d||p,...r.queuedTexts].filter(Boolean);u&&h.length&&(r.restored=po(u,h),await z()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await nC(u||s,f,{ts:r.startedAt,result:r.result,dropped:e,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await K({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:t,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),uo.get(t)===r&&uo.delete(t),o()}),r}async function nC(t,e,n){await I(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}async function rC(t){let e=Ad(await P(t));return e?.offerId?(await I(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 oC=[250,400,500,600];async function sC(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 oC){await Mn(r);let o=await wt(t.leadBox,{lines:Rt}).catch(()=>""),s=Jo(e.agent,o);if(s&&xl(n.text,s))return await bk(t.leadBox,e.agent,n.text.length+8),dt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),n.text}return null}var aC=3e4,iC=[350,650,900];async function cC(t,e){if(e?.agent!=="claude")return null;let n=t.events||[],r=[...n].reverse().find(s=>s.type==="said"&&s.text);if(!r||r.kind||Date.now()-r.at>aC||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Tt(r.text))return null;for(let s of iC){await Mn(s);let a;try{a=await Mu(e,{})}catch{return null}if(Im(a,r))return null;let i=await wt(t.leadBox,{lines:Rt}).catch(()=>"");if(Jo(e.agent,i))return null}return dt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}var lC=[250,450,700],Pd=2;async function uC(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=Pd;r++){let o=!1;for(let s of lC){await Mn(s);let a=await wt(t.leadBox,{lines:Rt}).catch(()=>""),i=Rm(e.agent,n,a,{busy:Wo(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Pd&&(await kt(t.leadBox,["Enter"],{kind:"submit-retry"}),K({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return K({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:Pd,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function bk(t,e,n){for(let r=0;r<3;r++){await Rr(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Mn(200);let o=await wt(t,{lines:Rt}).catch(()=>""),s=Jo(e,o);if(!s)return!0;n=s.length+8}return!1}function fo(t,e,{source:n}={}){t.draftRev=(t.draftRev||0)+1;let r=typeof e=="string"?e:"";return r.trim()?t.draft={text:r,updatedAt:Date.now(),...n?{source:n}:{}}:delete t.draft,t.draft||null}function po(t,e){let n=[],r=o=>n.some(s=>Tt(s).includes(Tt(o)));for(let o of[t.draft?.text,...e])o&&o.trim()&&!r(o)&&n.push(o);return n.length?fo(t,n.join(`
174
175
 
175
- `),{source:"restore"})?.text??null:null}var UO=12e4;function HO(t,e){let n=vt(e);if(!n)return!1;if(Od(t.id).some(o=>vt(o.text)===n))return!0;let r=t.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>UO)break;if(!(s.type!=="said"||!s.text||vt(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function ak(t,e,{rev:n=null}={}){if(typeof e!="string")throw new Error("text (string) required");let r=Z(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=e,s=zr(e);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&HO(r,s))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&!o.trim()&&r.draft?.text)return{ok:!0,draft:r.draft,rev:r.draftRev||0,ignored:"stale-clear"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let i=r.draft.text;vt(o).includes(vt(i))?lo(r,o):lo(r,`${i}
176
+ `),{source:"restore"})?.text??null:null}var dC=12e4;function fC(t,e){let n=Tt(e);if(!n)return!1;if(Cs(t.id).some(o=>Tt(o.text)===n))return!0;let r=t.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>dC)break;if(!(s.type!=="said"||!s.text||Tt(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function kk(t,e,{rev:n=null}={}){if(typeof e!="string")throw new Error("text (string) required");let r=tt(await W(),t);if(!r)throw new Error(`track "${t}" not found`);let o=e,s=Yr(e);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&fC(r,s))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&!o.trim()&&r.draft?.text)return{ok:!0,draft:r.draft,rev:r.draftRev||0,ignored:"stale-clear"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let i=r.draft.text;Tt(o).includes(Tt(i))?fo(r,o):fo(r,`${i}
176
177
 
177
- ${o}`,{source:"restore"})}else lo(r,o);return await V(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function ik(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Z(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await tt(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(f=>f.name===r.leadBox)?.agent||o.agent||"claude",i=sy(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await jo(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await pn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Cs(o.agent)});let u=await ay(a,r.leadBox,i);return dt(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await V(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function ck(t){for(let e of t){if(!e||!await li(e))continue;let n=await Ng(e).catch(()=>null);if(!n)continue;if((await F.stat(U.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function qt(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 bs(t){let e=[];for(let n of qt(t?.workspace)){if(!n?.dir)continue;let r=await Bu(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await ju(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function lk(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await Eo(e)??await Ac(e));let n=await ck([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 WO(t,e,n,r){let o=`track/${Wd(e)}-${t}`,s=[];try{await F.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await lk(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await et(c).catch(()=>null),u=U.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${U.basename(c)}-${p}`;a.add(u);let f=U.join(n,u),d=await ui(c);await Jr(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await Hi(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await ne(i.base,i.dir).catch(()=>{});throw await F.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var uk=t=>({branch:t.branch,kind:t.kind,repos:(t.repos??[]).filter(Boolean).map(({name:e,base:n,baseRef:r,repo:o})=>({name:e,base:n,baseRef:r,repo:o}))});async function dk(t,e){let n=await H(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await lk(typeof e=="string"?e.trim():e);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await et(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=U.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${U.basename(s)}-${d}`;let l=U.join(o.dir,c),u=await ui(s);await Pg(s,o.branch)?await Dg(s,l,o.branch):await Jr(s,l,o.branch,u);let f={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),f],await Hi(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"add",repo:a??s,name:c,workspace:uk(o)}),await V(),f}async function fk(t,e){let n=await H(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===e||c.repo===e||c.base===e));if(!s)throw new Error(`repo "${e}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of le.values())if(c.trackId===t&&!c.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before untagging");let a=await Bu(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await ju(s.dir,s.baseRef).catch(()=>0);if(a||i>0)throw new Error(`"${s.name}" has work (${a?"uncommitted changes":`${i} commit${i===1?"":"s"} ahead of ${s.baseRef??"base"}`}) \u2014 ship or discard it first`);return await ne(s.base,s.dir).catch(()=>{}),await F.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Hi(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:uk(o)}),await V(),{removed:s.name}}async function qO(t,e,{repo:n,repos:r,cwd:o}={}){await F.mkdir($d,{recursive:!0});let s=U.join($d,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return WO(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Eo(n)??await Ac(n));let i=await ck([n,o,sO]);if(i){let c=`track/${Wd(e)}-${t}`;try{let l=await ui(i);await Jr(i,s,c,l),await Md(s);let u=await et(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await F.mkdir(s,{recursive:!0}),await Bg(s),await Md(s),{dir:s,base:null,branch:null,kind:"repo"}}var Rs="# bostrat workspace",pk=`${Rs}
178
+ ${o}`,{source:"restore"})}else fo(r,o);return await z(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function xk(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=tt(await W(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await et(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(f=>f.name===r.leadBox)?.agent||o.agent||"claude",i=by(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await Uo(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await mn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:js(o.agent)});let u=await ky(a,r.leadBox,i);return dt(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await z(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function Sk(t){for(let e of t){if(!e||!await pi(e))continue;let n=await zg(e).catch(()=>null);if(!n)continue;if((await U.stat(H.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Gt(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:H.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function vs(t){let e=[];for(let n of Gt(t?.workspace)){if(!n?.dir)continue;let r=await Ku(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Gu(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function vk(t){let e=t;typeof e=="string"&&!H.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await Ro(e)??await $c(e));let n=await Sk([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 pC(t,e,n,r){let o=`track/${Yd(e)}-${t}`,s=[];try{await U.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await vk(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await rt(c).catch(()=>null),u=H.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${H.basename(c)}-${p}`;a.add(u);let f=H.join(n,u),d=await mi(c);await Xr(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await Gi(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await re(i.base,i.dir).catch(()=>{});throw await U.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var Tk=t=>({branch:t.branch,kind:t.kind,repos:(t.repos??[]).filter(Boolean).map(({name:e,base:n,baseRef:r,repo:o})=>({name:e,base:n,baseRef:r,repo:o}))});async function Ak(t,e){let n=await W(),r=tt(n,t);if(!r)throw new Error(`track "${t}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await vk(typeof e=="string"?e.trim():e);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await rt(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=H.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${H.basename(s)}-${d}`;let l=H.join(o.dir,c),u=await mi(s);await Jg(s,o.branch)?await Yg(s,l,o.branch):await Xr(s,l,o.branch,u);let f={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),f],await Gi(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Tk(o)}),await z(),f}async function _k(t,e){let n=await W(),r=tt(n,t);if(!r)throw new Error(`track "${t}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===e||c.repo===e||c.base===e));if(!s)throw new Error(`repo "${e}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of de.values())if(c.trackId===t&&!c.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before untagging");let a=await Ku(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Gu(s.dir,s.baseRef).catch(()=>0);if(a||i>0)throw new Error(`"${s.name}" has work (${a?"uncommitted changes":`${i} commit${i===1?"":"s"} ahead of ${s.baseRef??"base"}`}) \u2014 ship or discard it first`);return await re(s.base,s.dir).catch(()=>{}),await U.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Gi(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Tk(o)}),await z(),{removed:s.name}}async function mC(t,e,{repo:n,repos:r,cwd:o}={}){await U.mkdir(jd,{recursive:!0});let s=H.join(jd,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return pC(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!H.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Ro(n)??await $c(n));let i=await Sk([n,o,RO]);if(i){let c=`track/${Yd(e)}-${t}`;try{let l=await mi(i);await Xr(i,s,c,l),await Ld(s);let u=await rt(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await U.mkdir(s,{recursive:!0}),await Xg(s),await Ld(s),{dir:s,base:null,branch:null,kind:"repo"}}var Ns="# bostrat workspace",Ek=`${Ns}
178
179
 
179
180
  TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
180
181
  before substantive work \u2014 it is the shared cross-provider memory for this repo
@@ -320,11 +321,24 @@ the SAME runbook \u2014 keep completed steps, struck through (~~like this~~ \u20
320
321
  add a near-duplicate. When every step is confirmed, run
321
322
  \`bostrat-runbook done <name-or-id>\`. At the START of related work, check
322
323
  \`bostrat-runbook ls\` \u2014 if an open runbook covers this ground, update it rather
323
- than creating another. You are the only writer of a runbook's CONTENT \u2014 the
324
+ than creating another. \`ls --all\` spans EVERY node, not just this one, and
325
+ \`bostrat-runbook show <name-or-id>\` prints a runbook's content \u2014 read before
326
+ deciding a fresh one is needed. A runbook marked \`[node:<name>]\` was authored
327
+ on another node: you can read it, but revising it needs a Track on that node \u2014
328
+ say so in chat rather than authoring a near-duplicate here. You are the only
329
+ writer of a runbook's CONTENT \u2014 the
324
330
  operator reads, acts, and reports back in chat. They can, however, mark one
325
331
  done (or reopen it) themselves from the Runbooks tab, so a runbook you find
326
332
  already \`done\` was closed out by them: don't reopen it to add a victory lap.
327
333
 
334
+ When asked about scheduled or routine jobs ("loops"), check \`bostrat-loop ls\` \u2014
335
+ it lists every Loop for this owner across ALL nodes (\`bostrat-loop show
336
+ <name-or-id>\` prints one loop's prompt, schedule and recent runs; a loop's run
337
+ Track is named "\u21BB <loop name>"). Loops are operator-managed from the Loops tab:
338
+ you can read them but never create, edit, fire, or delete one \u2014 describe any
339
+ proposed change in chat instead. Never claim a loop does or doesn't exist from
340
+ Track names or memory alone; \`bostrat-loop ls\` is the authority.
341
+
328
342
  When something happens that the operator would want to know about RIGHT NOW and
329
343
  they are plausibly away from the screen, you can reach their phone:
330
344
 
@@ -352,9 +366,9 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
352
366
  statement, or omitted entirely renders no controls \u2014 so close EVERY completed
353
367
  implementation turn this way, every time. When the operator has already asked you
354
368
  to commit / push / open the PR, just do it; do not re-offer.
355
- `,KO=pk,As=pk+Om;async function Md(t){try{await F.writeFile(U.join(t,"AGENTS.md"),As,{flag:"wx"})}catch{return}await zO(t,["/AGENTS.md"])}var Id=t=>typeof t=="string"&&(t===Rs||t.startsWith(`${Rs}
356
- `)),GO="# bostrat multi-repo workspace";function VO(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
357
- `);return`${GO}
369
+ `,hC=Ek,Rs=Ek+Fm;async function Ld(t){try{await U.writeFile(H.join(t,"AGENTS.md"),Rs,{flag:"wx"})}catch{return}await yC(t,["/AGENTS.md"])}var Dd=t=>typeof t=="string"&&(t===Ns||t.startsWith(`${Ns}
370
+ `)),wC="# bostrat multi-repo workspace";function gC(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
371
+ `);return`${wC}
358
372
 
359
373
  This directory is NOT a git repo. It is a bostrat Track workspace holding one
360
374
  git worktree per tagged repo, ALL on the same branch \`${e}\`:
@@ -366,25 +380,25 @@ push / open PRs) in each repo separately, always on that shared branch. Work
366
380
  only needs to land in the repos it actually touches \u2014 an untouched repo's
367
381
  branch simply stays at its fork point and never gets pushed. When asked to run
368
382
  or preview an app, run it from that app's own repo directory.
369
- `}async function Hi(t,e,n){let r=VO(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await F.writeFile(U.join(t,o),r).catch(()=>{})}var Rd=As.replace(`${Rs}
370
- `,`${Rs}
383
+ `}async function Gi(t,e,n){let r=gC(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await U.writeFile(H.join(t,o),r).catch(()=>{})}var Bd=Rs.replace(`${Ns}
384
+ `,`${Ns}
371
385
 
372
386
  (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
373
387
  SCOPE: these instructions apply ONLY when your working directory is under
374
388
  ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
375
389
  When working anywhere else, ignore everything below.
376
- `);async function mk(t){let{stdout:e}=await rO("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function zO(t,e){try{let n=await mk(t);await F.mkdir(U.dirname(n),{recursive:!0});let r=await F.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
377
- `).includes(s));o.length&&await F.appendFile(n,(r.endsWith(`
390
+ `);async function Ik(t){let{stdout:e}=await EO("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return H.resolve(t,e.trim())}async function yC(t,e){try{let n=await Ik(t);await U.mkdir(H.dirname(n),{recursive:!0});let r=await U.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
391
+ `).includes(s));o.length&&await U.appendFile(n,(r.endsWith(`
378
392
  `)||!r?"":`
379
393
  `)+o.join(`
380
394
  `)+`
381
- `)}catch{}}async function JO(t,e){try{let n=await mk(t),r=await F.readFile(n,"utf8"),o=r.split(`
395
+ `)}catch{}}async function bC(t,e){try{let n=await Ik(t),r=await U.readFile(n,"utf8"),o=r.split(`
382
396
  `).filter(s=>s!==e);o.length!==r.split(`
383
- `).length&&await F.writeFile(n,o.join(`
384
- `))}catch{}}async function hk(){let t={refreshed:0,removed:0,codexGlobal:null};try{await F.mkdir(Qe,{recursive:!0}),await F.writeFile(U.join(Qe,"CLAUDE.md"),KO).catch(()=>{}),await F.writeFile(U.join(Qe,"AGENTS.md"),As).catch(()=>{});let e=process.env.CODEX_HOME||U.join(process.env.HOME||"/tmp",".codex");if((await F.stat(e).catch(()=>null))?.isDirectory()){let r=U.join(e,"AGENTS.md"),o=await F.readFile(r,"utf8").catch(()=>null);o===null?(await F.writeFile(r,Rd,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Id(o)&&o!==Rd&&(await F.writeFile(r,Rd).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[$d,fo]){let o=await F.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 F.readFile(i,"utf8").catch(()=>null);c!==null&&Id(c)&&c!==As&&(await F.writeFile(i,As).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await F.readFile(l,"utf8").catch(()=>null);u!==null&&Id(u)&&(await F.rm(l,{force:!0}).catch(()=>{}),await JO(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function XO(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of qt(e))n.base&&n.dir&&await ne(n.base,n.dir).catch(()=>{});await F.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await ne(e.base,e.dir):await F.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function jd({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 ji(o,s),i=await H(),c=Ms().slice(0,8),l=await qO(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(h=>h.name))),f=`${t.trim()} lead`;u.has(qc(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await qn(f,l.dir,{agentCmd:a,agent:o});Aa({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a});let p={id:c,name:t.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[Ak(d,"lead","live")],events:[],createdAt:Date.now()};return dt(p,"opened",{agent:o,box:d.name,skipPermissions:s,name:p.name,cwd:p.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:h,baseRef:y,repo:k})=>({name:m,base:h,baseRef:y,repo:k}))}:{}}}),i.tracks.push(p),await V(),tn(p)}var YO=new Set(["claude","codex","cursor"]);async function wk({provider:t="claude",fresh:e=!1}={}){if(!YO.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 tn(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await mo(r.id).catch(()=>{})}let o=(await ji(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Ms().slice(0,8),a=U.join(Qe,"setup",s);await F.mkdir(a,{recursive:!0});let i=await qn(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&Aa({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:[Ak(i,"lead","live")],events:[],createdAt:Date.now()};return dt(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 V(),tn(c)}async function gk(t,e,n){await Zh(t).catch(()=>{});let r=$s.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),$s.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of ho(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Kd(a).catch(()=>{}),le.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 we(a.name).catch(()=>{})}async function yk(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 Ch(s),ok(t),await gk(t,s,"track deleted"),n?await lw(t).catch(()=>{}):await cw(t).catch(()=>{}),await cy(),await XO(s),e&&await Nh(t).catch(()=>{}),e?await Ob(t).catch(()=>{}):await $b(s).catch(()=>{}),dt(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await V(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}var Ni=new Set;async function mo(t){let e=await H(),n=Z(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);return n.archivedAt=Date.now(),ok(t),dt(n,"archived",{boxes:n.boxes.length}),await V(),n.kind==="setup"?(await bk(t),tn(n)):(Ni.add(t),QO(t).catch(()=>{}).finally(()=>Ni.delete(t)),{...await tn(n),archiving:!0})}var ZO=9e4;async function QO(t){try{await tM(t)}catch{}await bk(t).catch(()=>{})}async function bk(t){let e=await H(),n=Z(e,t);if(!(!n||!n.archivedAt)){await gk(t,n,"track archived");for(let r of n.boxes)mr.has(r.state)||(r.state="stopped");await V()}}async function tM(t){let e=await H(),n=Z(e,t);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await tt(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Qm(n.leadBox,i.cwd,{timeoutMs:ZO})),s="lead-cli"}catch{}if(r||(r=wu(n,await B(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await $(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await V()}async function kk(){let t=await H(),e=0,n=0,r=0;for(let o of t.tracks){if(!o.archivedAt||o.kind==="setup"||o.beat?.text){n++;continue}let s=wu(o,await B(o).catch(()=>[]));if(!s){r++;continue}o.beat={text:s,source:"derived",at:Date.now()},await $(o,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",payload:{text:s,source:"derived",backfill:!0}}).catch(()=>{}),e++}return await V(),{recorded:e,skipped:n,empty:r}}async function In(t,e){let n=await Bt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await xk(n);if(r.length){for(let o of r)await rr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await mo(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function xk(t){return(await Ee().catch(()=>[])).filter(n=>n.trackId===t.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===t.repo&&n.number===t.number))}async function Rn(t,e){return Ld(t,e,"merged")}async function Ld(t,e,n="merged"){let r=await Bt(t,e).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await xk(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Jw(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await ue(r.trackId,eM(a),{kind:"merge-notify"}),!0}catch{return!1}}var Hb={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function eM(t){let e=i=>`${i.repo?`${i.repo}`:""}#${i.number}${i.title?` "${i.title}"`:""}${i.url?` (${i.url})`:""}`;if(t.length===1){let i=t[0],c=`#${i.number}${i.title?` "${i.title}"`:""}`;return i.outcome!=="merged"?`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} was ${Hb[i.outcome]??i.outcome}. This is an automated notification: the work did NOT land \u2014 check whether it still needs to ship another way. If not, say so briefly.`:`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} has merged. This is an automated merge notification: pick up whatever follow-up work this merge unblocks (dependent tasks, cleanup, next steps that were waiting on it). If nothing needs doing, say so briefly.`}let n=t.filter(i=>i.outcome==="merged"),r=t.filter(i=>i.outcome!=="merged"),o=r.length?`${n.length} of the ${t.length} PRs on this Track merged; ${r.length} did not land:`:`All ${t.length} PRs on this Track have merged:`,s=t.map(i=>`- ${e(i)} \u2014 ${Hb[i.outcome]??i.outcome}`).join(`
397
+ `).length&&await U.writeFile(n,o.join(`
398
+ `))}catch{}}async function Rk(){let t={refreshed:0,removed:0,codexGlobal:null};try{await U.mkdir(tn,{recursive:!0}),await U.writeFile(H.join(tn,"CLAUDE.md"),hC).catch(()=>{}),await U.writeFile(H.join(tn,"AGENTS.md"),Rs).catch(()=>{});let e=process.env.CODEX_HOME||H.join(process.env.HOME||"/tmp",".codex");if((await U.stat(e).catch(()=>null))?.isDirectory()){let r=H.join(e,"AGENTS.md"),o=await U.readFile(r,"utf8").catch(()=>null);o===null?(await U.writeFile(r,Bd,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Dd(o)&&o!==Bd&&(await U.writeFile(r,Bd).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[jd,mo]){let o=await U.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=H.join(r,s.name),i=H.join(a,"AGENTS.md"),c=await U.readFile(i,"utf8").catch(()=>null);c!==null&&Dd(c)&&c!==Rs&&(await U.writeFile(i,Rs).catch(()=>{}),t.refreshed++);let l=H.join(a,"CLAUDE.md"),u=await U.readFile(l,"utf8").catch(()=>null);u!==null&&Dd(u)&&(await U.rm(l,{force:!0}).catch(()=>{}),await bC(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function kC(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of Gt(e))n.base&&n.dir&&await re(n.base,n.dir).catch(()=>{});await U.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await re(e.base,e.dir):await U.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Kd({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 Hi(o,s),i=await W(),c=Bs().slice(0,8),l=await mC(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(h=>h.name))),f=`${t.trim()} lead`;u.has(Jc(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await Kn(f,l.dir,{agentCmd:a,agent:o});$a({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a});let p={id:c,name:t.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[Lk(d,"lead","live")],events:[],createdAt:Date.now()};return dt(p,"opened",{agent:o,box:d.name,skipPermissions:s,name:p.name,cwd:p.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:h,baseRef:g,repo:k})=>({name:m,base:h,baseRef:g,repo:k}))}:{}}}),i.tracks.push(p),await z(),en(p)}var xC=new Set(["claude","codex","cursor"]);async function $k({provider:t="claude",fresh:e=!1}={}){if(!xC.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await W()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await en(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await ho(r.id).catch(()=>{})}let o=(await Hi(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Bs().slice(0,8),a=H.join(tn,"setup",s);await U.mkdir(a,{recursive:!0});let i=await Kn(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&$a({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:[Lk(i,"lead","live")],events:[],createdAt:Date.now()};return dt(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 W()).tracks.push(c),await z(),en(c)}async function Ok(t,e,n){await aw(t).catch(()=>{});let r=Ps.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Ps.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of wo(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Zd(a).catch(()=>{}),de.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 W(),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 ye(a.name).catch(()=>{})}async function Ck(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await W(),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 Uh(s),yk(t),await Ok(t,s,"track deleted"),n?await bw(t).catch(()=>{}):await yw(t).catch(()=>{}),await Sy(),await kC(s),e&&await Hh(t).catch(()=>{}),e?await Kb(t).catch(()=>{}):await qb(s).catch(()=>{}),dt(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await z(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}var ji=new Set;async function ho(t){let e=await W(),n=tt(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);return n.archivedAt=Date.now(),yk(t),dt(n,"archived",{boxes:n.boxes.length}),await z(),n.kind==="setup"?(await Mk(t),en(n)):(ji.add(t),vC(t).catch(()=>{}).finally(()=>ji.delete(t)),{...await en(n),archiving:!0})}var SC=9e4;async function vC(t){try{await TC(t)}catch{}await Mk(t).catch(()=>{})}async function Mk(t){let e=await W(),n=tt(e,t);if(!(!n||!n.archivedAt)){await Ok(t,n,"track archived");for(let r of n.boxes)hr.has(r.state)||(r.state="stopped");await z()}}async function TC(t){let e=await W(),n=tt(e,t);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await et(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await ch(n.leadBox,i.cwd,{timeoutMs:SC})),s="lead-cli"}catch{}if(r||(r=vu(n,await P(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await I(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await z()}async function Nk(){let t=await W(),e=0,n=0,r=0;for(let o of t.tracks){if(!o.archivedAt||o.kind==="setup"||o.beat?.text){n++;continue}let s=vu(o,await P(o).catch(()=>[]));if(!s){r++;continue}o.beat={text:s,source:"derived",at:Date.now()},await I(o,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",payload:{text:s,source:"derived",backfill:!0}}).catch(()=>{}),e++}return await z(),{recorded:e,skipped:n,empty:r}}async function Rn(t,e){let n=await Lt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Pk(n);if(r.length){for(let o of r)await or(o.repo,o.number,!0).catch(()=>{});return!1}try{return await ho(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Pk(t){return(await $e().catch(()=>[])).filter(n=>n.trackId===t.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===t.repo&&n.number===t.number))}async function $n(t,e){return Gd(t,e,"merged")}async function Gd(t,e,n="merged"){let r=await Lt(t,e).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await Pk(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await sg(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await fe(r.trackId,AC(a),{kind:"merge-notify"}),!0}catch{return!1}}var nk={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function AC(t){let e=i=>`${i.repo?`${i.repo}`:""}#${i.number}${i.title?` "${i.title}"`:""}${i.url?` (${i.url})`:""}`;if(t.length===1){let i=t[0],c=`#${i.number}${i.title?` "${i.title}"`:""}`;return i.outcome!=="merged"?`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} was ${nk[i.outcome]??i.outcome}. This is an automated notification: the work did NOT land \u2014 check whether it still needs to ship another way. If not, say so briefly.`:`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} has merged. This is an automated merge notification: pick up whatever follow-up work this merge unblocks (dependent tasks, cleanup, next steps that were waiting on it). If nothing needs doing, say so briefly.`}let n=t.filter(i=>i.outcome==="merged"),r=t.filter(i=>i.outcome!=="merged"),o=r.length?`${n.length} of the ${t.length} PRs on this Track merged; ${r.length} did not land:`:`All ${t.length} PRs on this Track have merged:`,s=t.map(i=>`- ${e(i)} \u2014 ${nk[i.outcome]??i.outcome}`).join(`
385
399
  `),a=n.length?r.length?`This is an automated merge notification: pick up whatever follow-up work the merged PR${n.length===1?"":"s"} unblock${n.length===1?"s":""} \u2014 and note the unmerged one${r.length===1?"":"s"}: that work did NOT land, so check whether it still needs to ship. If nothing needs doing, say so briefly.`:"This is an automated merge notification: pick up whatever follow-up work these merges unblock (dependent tasks, cleanup, next steps that were waiting on them). If nothing needs doing, say so briefly.":"This is an automated notification: NONE of the PRs landed \u2014 the work did not ship. Check whether it still needs to go out another way. If not, say so briefly.";return`${o}
386
400
  ${s}
387
- ${a}`}async function Fd(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||mr.has(e.state)||await tt(e.name))return!1;let n=Kb(e.agent,e.sessionId),r=await wa(n);if(r===Tr)return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===Ar&&console.warn(`[track] ${e.name}: could not confirm "${e.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await qn(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=Ao(o.hookToken),Aa({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",dt(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(o){return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(o?.message||o)}),!1}}async function vk(t){let e=await H(),n=Z(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,Ni.delete(t),dt(n,"unarchived",{}),await Fd(n),await V(),tn(n)}async function Sk(){let t=await H(),e=new Set((await Ce()).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 we(a.name).catch(()=>{}),dt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!mr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await V(),r}async function Tk(t){let e=await H(),n=Z(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(mr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await tt(n.leadBox)){let a=await mt(n.leadBox,{lines:_t}).catch(()=>"");if(Sa(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await we(n.leadBox),r.state="stopped"}let s=await Fd(n);if(await V(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return tn(n)}var Ak=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:Ao(t.hookToken),createdAt:Date.now()});function _k(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 $s=new Map,Ns=t=>$s.get(t)||($e?Z($e,t)?.handoff:null)||null;async function Pi(t,e){let n=await H(),r=Z(n,t);r&&(r.handoff={...r.handoff||{},...e},await V())}function nM(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 Ek(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=nM(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await tt(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(),f=_k(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:Li,at:u},dt(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:f.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 q({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 q({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await we(i).catch(()=>{}),a&&await we(a).catch(()=>{})}return e&&await V(),e}async function Ud(t,e,n=!0,r=null){let o=$s.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Z(await H(),t);if(!s)throw new Error(`track "${t}" not found`);Ui(t,"handoff");let a=await ji(e,n),i=Zr(e);if(i){let u=new Error(`can't hand off to ${e} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await tt(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:Gb(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return $s.set(t,l),await Pi(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),rM(l,s,c,a,e).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${l.step}": ${l.error}`),await Pi(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),q({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),q({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function rM(t,e,n,r,o){let s=async(R,z={})=>{t.step=R,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${R}`),await Pi(t.trackId,{step:R,toBox:t.toBox}),await q({flow:"lead-handoff",phase:R,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:z.sessionName,...z})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await H(),i=Z(a,t.trackId)?.boxes.find(R=>R.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:t.fromBox}),t.checkpoint="llm",Zr(c))t.checkpoint="skipped-capped";else try{await ah(t.fromBox,{timeoutMs:iO})}catch(R){t.checkpoint="reconstructed",await q({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:t.trackId,fromBox:t.fromBox,error:R?.message||String(R)}).catch(()=>{})}t.checkpoint!=="llm"&&(t.statusArtifact=await ih(t.trackId,n.cwd,{trackName:e.name||e.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:t.fromBox,checkpoint:t.checkpoint,statusArtifact:t.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await yu(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await pn({cwd:n.cwd,sessionId:d.sessionId,sessionName:t.fromBox,agent:o,agentCmd:Cs(o)});let p=await mt(t.fromBox,{lines:_t}).catch(()=>"");if(!Ta(o,p)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let R=Kb(c,l,{skipPermissions:t.skipPermissions});await yu(t.fromBox,{agentCmd:R,agent:c,resumeSessionId:l,pinned:u});let z=await H(),O=Z(z,t.trackId);if(O){let M=O.boxes.find(S=>S.name===t.fromBox);M&&(M.state="live"),dt(O,"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 V()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let m=await H(),h=Z(m,t.trackId);if(!h)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=h.boxes.find(R=>R.name===t.fromBox);if(!y)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let k=Date.now(),v=_k(y),I=v[v.length-1];I.endedAt=k,t.reason&&(I.endReason=t.reason),v.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:k,endedAt:null}),y.agent=o,y.sessionId=d.sessionId,y.pinned=d.pinned,y.state="live",y.role="lead",h.pendingHandoffHydrate={artifact:t.statusArtifact||Li,at:k},dt(h,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:v.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt,checkpoint:t.checkpoint}),await V(),await s("done",{sessionName:t.fromBox}),await Pi(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var oM=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,sM=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,fo=U.join(Qe,"worktrees"),ao=[_s,Ci],le=new Map;var ho=t=>{let e=new Map([...le.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=$e?Z($e,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)},Hd=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,repos:t.repos||null,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 aM(t,e,n){let r=await H(),o=Z(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await V())}var po=t=>aM(t.trackId,t.id,Hd(t));async function io(t,e){t.step=e,await po(t)}function iM(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 cM(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function lM(t,e,n){if(!cM(n)||le.has(e)||!await tt(n.box).catch(()=>null))return!1;try{await F.access(n.dir)}catch{return!1}let o=await tt(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return le.set(e,s),Mk(s,o).catch(a=>$k(s,a)),!0}async function Ik(){let t=await H(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(le.has(r))continue;if(await lM(n,r,o)){e=!0,dt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await q({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=iM(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!mr.has(i.state)&&(i.state="canceled"),dt(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 q({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await Wb(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||mr.has(r.state)||le.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?U.join(fo,`${n.id}-${r.subtaskId}`):null;await q({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await Wb(n,r.name,o)}}return e=await uM(t)||e,e&&await V(),e}async function Wb(t,e,n){if(e&&await we(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of qt(t.workspace)){let s=U.join(n,o.name);await ne(o.base,s).then(()=>!0,()=>!1)||await ne(o.dir,s).catch(()=>{})}await F.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await tt(t.leadBox).catch(()=>null);r?.cwd&&await ne(r.cwd,n).catch(()=>{})}async function uM(t){let e;try{e=await F.readdir(fo)}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 le.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(fo,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of qt(a.workspace))await ne(i.base,U.join(s,i.name)).catch(()=>{});await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await tt(a.leadBox).catch(()=>null):null;i?.cwd?await ne(i.cwd,s).catch(()=>{}):await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await q({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Wd=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Os=class extends Error{};async function Rk(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!zr(n))throw new Error("task (string) required");let s=await ji(e,r),a=Zr(e);if(a){let p=new Error(`can't delegate to ${e} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=Z(await H(),t);if(!i)throw new Error(`track "${t}" not found`);let c=await tt(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=qt(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of p)if(!m?.dir||!await li(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);u=p.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await li(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Ms().slice(0,8),d={id:f,trackId:t,trackName:i.name,agent:e,task:zr(n),skipPermissions:r,reason:Gb(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Wd(i.name)}-${f}`,base:null,repos:u,dir:U.join(fo,`${t}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return le.set(f,d),await po(d),dM(d,c,s).catch(p=>$k(d,p)),d}async function $k(t,e){t.failedStep=t.step,t.error=e instanceof Os?"canceled":e?.message||String(e),t.step=e instanceof Os?"canceled":"error",t.finishedAt=Date.now(),await Kd(t).catch(()=>{}),await po(t).catch(()=>{}),await hM(t).catch(()=>{})}function qd(t){let e=le.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 Ok(t){let e=le.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 dM(t,e,n){if(await io(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await ms(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await po(t)}else t.base=await ms(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await io(t,"spawn"),await F.mkdir(fo,{recursive:!0}),t.repos){await F.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Jr(a.leadDir,U.join(t.dir,a.name),t.branch,a.base);let s=qt((Z(await H(),t.trackId)||{}).workspace);await Hi(t.dir,t.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),t.branch)}else await Jr(e.cwd,t.dir,t.branch,t.base),await Md(t.dir);await fM(t,e);let r=t.trackName.slice(0,38).trim(),o=await qn(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await pM(t,o),await Vb(t.trackId,o.name,uO(t.task,!!t.repos),{kind:"subtask"}),await Mk(t,e)}async function Mk(t,e){await io(t,"working");let n=U.join(t.dir,Ci);if(await gM(n,t),t.canceled)throw new Os;if(await io(t,"merge"),t.repos){let o={files:0,insertions:0,deletions:0},s=[],a=[],i=!0;for(let c of t.repos){let l=U.join(t.dir,c.name);await Du(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await Fu(c.leadDir,c.base,t.branch,ao),c.files=await Uu(c.leadDir,c.base,t.branch,ao);let u=await Hu(c.leadDir,c.base,t.branch,ao);c.applied=u.applied,c.conflicts=u.conflicts??[],o.files+=c.stat.files,o.insertions+=c.stat.insertions,o.deletions+=c.stat.deletions,s.push(...c.files.map(f=>U.join(c.name,f))),a.push(...c.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${c.name}: ${f}`:U.join(c.name,f))),u.applied||(i=!1)}t.stat=o,t.files=s,t.conflicts=a,t.applied=i}else{await Du(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Fu(e.cwd,t.base,t.branch,ao),t.files=await Uu(e.cwd,t.base,t.branch,ao);let o=await Hu(e.cwd,t.base,t.branch,ao);t.applied=o.applied,t.conflicts=o.conflicts}await po(t);let r=await wM(t,e,n);await io(t,"resume"),await Vb(t.trackId,t.leadBox,dO(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await io(t,"reap"),await Kd(t),t.step="done",t.finishedAt=Date.now(),await mM(t),await po(t)}async function fM(t,e){let n=U.join(t.dir,U.dirname(_s));await F.mkdir(n,{recursive:!0});let r="";try{r=await Pp(e.cwd,e.sessionId)}catch{}if(!r)try{r=await F.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}
401
+ ${a}`}async function Vd(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||hr.has(e.state)||await et(e.name))return!1;let n=sk(e.agent,e.sessionId),r=await xa(n);if(r===Ar)return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===_r&&console.warn(`[track] ${e.name}: could not confirm "${e.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await Kn(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=Eo(o.hookToken),$a({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",dt(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(o){return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(o?.message||o)}),!1}}async function Dk(t){let e=await W(),n=tt(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,ji.delete(t),dt(n,"unarchived",{}),await Vd(n),await z(),en(n)}async function Bk(){let t=await W(),e=new Set((await ae()).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 ye(a.name).catch(()=>{}),dt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!hr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await z(),r}async function zd(t){let e=await W(),n=tt(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(hr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await et(n.leadBox)){let a=await wt(n.leadBox,{lines:Rt}).catch(()=>"");if(Ia(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await ye(n.leadBox),r.state="stopped"}let s=await Vd(n);if(await z(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return en(n)}async function jk(t,e,n){let r=await Z(t);if(!r)throw new Error(`track "${t}" not found`);if(!e)throw new Error("unknown offer");let o=await P(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===e))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===e))throw new Error("offer already answered");return n&&await zd(t),await I(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:e,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}var Lk=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:Eo(t.hookToken),createdAt:Date.now()});function Fk(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 Ps=new Map,Ls=t=>Ps.get(t)||(Me?tt(Me,t)?.handoff:null)||null;async function Li(t,e){let n=await W(),r=tt(n,t);r&&(r.handoff={...r.handoff||{},...e},await z())}function _C(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 Uk(){let t=await W(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=_C(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await et(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(),f=Fk(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:Wi,at:u},dt(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:f.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 K({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 K({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await ye(i).catch(()=>{}),a&&await ye(a).catch(()=>{})}return e&&await z(),e}async function Jd(t,e,n=!0,r=null){let o=Ps.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=tt(await W(),t);if(!s)throw new Error(`track "${t}" not found`);Ki(t,"handoff");let a=await Hi(e,n),i=to(e);if(i){let u=new Error(`can't hand off to ${e} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await et(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:ak(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Ps.set(t,l),await Li(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),EC(l,s,c,a,e).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${l.step}": ${l.error}`),await Li(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),K({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),K({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function EC(t,e,n,r,o){let s=async($,J={})=>{t.step=$,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${$}`),await Li(t.trackId,{step:$,toBox:t.toBox}),await K({flow:"lead-handoff",phase:$,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:J.sessionName,...J})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await W(),i=tt(a,t.trackId)?.boxes.find($=>$.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:t.fromBox}),t.checkpoint="llm",to(c))t.checkpoint="skipped-capped";else try{await hh(t.fromBox,{timeoutMs:OO})}catch($){t.checkpoint="reconstructed",await K({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:t.trackId,fromBox:t.fromBox,error:$?.message||String($)}).catch(()=>{})}t.checkpoint!=="llm"&&(t.statusArtifact=await wh(t.trackId,n.cwd,{trackName:e.name||e.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:t.fromBox,checkpoint:t.checkpoint,statusArtifact:t.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await Au(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await mn({cwd:n.cwd,sessionId:d.sessionId,sessionName:t.fromBox,agent:o,agentCmd:js(o)});let p=await wt(t.fromBox,{lines:Rt}).catch(()=>"");if(!Ra(o,p)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let $=sk(c,l,{skipPermissions:t.skipPermissions});await Au(t.fromBox,{agentCmd:$,agent:c,resumeSessionId:l,pinned:u});let J=await W(),O=tt(J,t.trackId);if(O){let N=O.boxes.find(A=>A.name===t.fromBox);N&&(N.state="live"),dt(O,"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 z()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let m=await W(),h=tt(m,t.trackId);if(!h)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=h.boxes.find($=>$.name===t.fromBox);if(!g)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let k=Date.now(),S=Fk(g),R=S[S.length-1];R.endedAt=k,t.reason&&(R.endReason=t.reason),S.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:k,endedAt:null}),g.agent=o,g.sessionId=d.sessionId,g.pinned=d.pinned,g.state="live",g.role="lead",h.pendingHandoffHydrate={artifact:t.statusArtifact||Wi,at:k},dt(h,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:S.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt,checkpoint:t.checkpoint}),await z(),await s("done",{sessionName:t.fromBox}),await Li(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var IC=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,RC=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,mo=H.join(tn,"worktrees"),co=[$s,Bi],de=new Map;var wo=t=>{let e=new Map([...de.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=Me?tt(Me,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)},Vi=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,repos:t.repos||null,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 Hk(t,e,n){let r=await W(),o=tt(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await z())}var Fs=t=>Hk(t.trackId,t.id,Vi(t));async function lo(t,e){t.step=e,await Fs(t)}function $C(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 OC(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function CC(t,e,n){if(!OC(n)||de.has(e)||!await et(n.box).catch(()=>null))return!1;try{await U.access(n.dir)}catch{return!1}let o=await et(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return de.set(e,s),Vk(s,o).catch(a=>Kk(s,a)),!0}async function Wk(){let t=await W(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(de.has(r))continue;if(await CC(n,r,o)){e=!0,dt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await K({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=$C(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!hr.has(i.state)&&(i.state="canceled"),dt(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 K({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await rk(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||hr.has(r.state)||de.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?H.join(mo,`${n.id}-${r.subtaskId}`):null;await K({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await rk(n,r.name,o)}}return e=await MC(t)||e,e&&await z(),e}async function rk(t,e,n){if(e&&await ye(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of Gt(t.workspace)){let s=H.join(n,o.name);await re(o.base,s).then(()=>!0,()=>!1)||await re(o.dir,s).catch(()=>{})}await U.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await et(t.leadBox).catch(()=>null);r?.cwd&&await re(r.cwd,n).catch(()=>{})}async function MC(t){let e;try{e=await U.readdir(mo)}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 de.values())o.dir&&n.add(H.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=H.join(mo,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Gt(a.workspace))await re(i.base,H.join(s,i.name)).catch(()=>{});await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await et(a.leadBox).catch(()=>null):null;i?.cwd?await re(i.cwd,s).catch(()=>{}):await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await K({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Yd=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Ds=class extends Error{};async function qk(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Yr(n))throw new Error("task (string) required");let s=await Hi(e,r),a=to(e);if(a){let p=new Error(`can't delegate to ${e} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=tt(await W(),t);if(!i)throw new Error(`track "${t}" not found`);let c=await et(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=Gt(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of p)if(!m?.dir||!await pi(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);u=p.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await pi(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Bs().slice(0,8),d={id:f,trackId:t,trackName:i.name,agent:e,task:Yr(n),skipPermissions:r,reason:ak(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Yd(i.name)}-${f}`,base:null,repos:u,dir:H.join(mo,`${t}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return de.set(f,d),await Fs(d),NC(d,c,s).catch(p=>Kk(d,p)),d}async function Kk(t,e){t.failedStep=t.step,t.error=e instanceof Ds?"canceled":e?.message||String(e),t.step=e instanceof Ds?"canceled":"error",t.finishedAt=Date.now(),await Zd(t).catch(()=>{}),await Fs(t).catch(()=>{}),await jC(t).catch(()=>{})}function Xd(t){let e=de.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 Gk(t){let e=de.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 NC(t,e,n){if(await lo(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await ys(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await Fs(t)}else t.base=await ys(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await lo(t,"spawn"),await U.mkdir(mo,{recursive:!0}),t.repos){await U.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Xr(a.leadDir,H.join(t.dir,a.name),t.branch,a.base);let s=Gt((tt(await W(),t.trackId)||{}).workspace);await Gi(t.dir,t.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),t.branch)}else await Xr(e.cwd,t.dir,t.branch,t.base),await Ld(t.dir);await PC(t,e);let r=t.trackName.slice(0,38).trim(),o=await Kn(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await DC(t,o),await ik(t.trackId,o.name,NO(t.task,!!t.repos),{kind:"subtask"}),await Vk(t,e)}async function Vk(t,e){await lo(t,"working");let n=H.join(t.dir,Bi);if(await FC(n,t),t.canceled)throw new Ds;if(await lo(t,"merge"),t.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of t.repos){let u=H.join(t.dir,l.name);await qu(u,`bostrat: subtask ${t.id} result (auto-committed)`),l.stat=await zu(l.leadDir,l.base,t.branch,co),l.files=await Ju(l.leadDir,l.base,t.branch,co);let f=await Yu(l.leadDir,l.base,t.branch,co);l.applied=f.applied,l.conflicts=f.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(d=>H.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:H.join(l.name,d))),f.applied||(c=!1)}t.stat=s,t.files=a,t.conflicts=i,t.applied=c}else{await qu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await zu(e.cwd,t.base,t.branch,co),t.files=await Ju(e.cwd,t.base,t.branch,co);let s=await Yu(e.cwd,t.base,t.branch,co);t.applied=s.applied,t.conflicts=s.conflicts}await Fs(t);let r=await LC(t,e,n);await lo(t,"resume"),await ik(t.trackId,t.leadBox,PO(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await lo(t,"reap"),await Zd(t),t.step="done";let o=Date.now();await Hk(t.trackId,t.id,{...Vi(t),finishedAt:o}),await BC(t,o),t.finishedAt=o}async function PC(t,e){let n=H.join(t.dir,H.dirname($s));await U.mkdir(n,{recursive:!0});let r="";try{r=await qp(e.cwd,e.sessionId)}catch{}if(!r)try{r=await U.readFile(H.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}
388
402
 
389
403
  **Delegated task:** ${t.task}
390
404
 
@@ -395,50 +409,50 @@ ${a}`}async function Fd(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name
395
409
  ## Conversation so far (lead \u2192 you)
396
410
 
397
411
  ${r}
398
- `;await F.writeFile(U.join(t.dir,_s),o)}async function pM(t,e){let n=await H(),r=Z(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:Ao(e.hookToken),createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),dt(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]||{},...Hd(t)},await V())}async function mM(t){let e=await H(),n=Z(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),dt(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 V()}async function hM(t){let e=await H(),n=Z(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"),dt(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]||{},...Hd(t)},await V()}async function wM(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=U.join(e.cwd,r);await F.mkdir(U.dirname(o),{recursive:!0}),await F.copyFile(n,o)}catch{}return r}async function Kd(t){if(t.box&&await we(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await ne(e.leadDir,U.join(t.dir,e.name)).catch(()=>{});await F.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await tt(t.leadBox).catch(()=>null))?.cwd;n&&await ne(n,t.dir).catch(()=>{})}}async function gM(t,e){let n=(e.startedAt||Date.now())+oM;for(;;){if(e.canceled||e.forceMerge)return;try{await F.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Mn(sM)}}function Mn(t){return new Promise(e=>setTimeout(e,t))}import Ps from"node:fs/promises";import Ki from"node:path";import vM from"node:os";import yM from"ws";var Ck=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Wi(t,e,n={}){let r=await fetch(`${Ck()}/${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 Gd=(t,e)=>Wi("chat.postMessage",t,e),wo=(t,e)=>Wi("chat.update",t,e);async function Kt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function bM(t,e,n={}){let r=await fetch(`${Ck()}/${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 Nk(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await bM("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 Wi("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var kM=2200;function Pk({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 Wi("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new yM(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=h=>{try{o.send(JSON.stringify(h?{envelope_id:f.envelope_id,payload:h}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},kM);Promise.resolve().then(()=>e(f)).then(h=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?h:void 0))},h=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${h?.message||h}`)})}),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 jk=()=>process.env.BOSTRAT_STATE_DIR||Ki.join(process.env.HOME||vM.tmpdir(),".bostrat"),Yd=()=>Ki.join(jk(),"slack.json"),qi=null,Vd=null,zd=Promise.resolve();async function Qd(){return qi||(Vd??=Ps.readFile(Yd(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>qi=t).finally(()=>Vd=null),Vd)}async function Dk(){let t=JSON.stringify({threads:qi},null,2),e=`${Yd()}.${process.pid}.tmp`;await Ps.mkdir(jk(),{recursive:!0}),await Ps.writeFile(e,t),await Ps.rename(e,Yd())}async function en(){zd=zd.then(Dk,Dk),await zd.catch(()=>{})}async function tf(t){return(await Qd())[t]??null}async function SM(t,e){let n=await Qd();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var Y=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function Ds(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function TM(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Ds(n.replace(/\n+/g," "),e)}function Lk(t){let e=`*${Y(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${Y(t.repoLabel)} \xB7 \`${Y(t.branch||"?")}\` \xB7 lead *${Y(t.lead)}*`,r=t.kickoff?`> ${Y(Ds(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
399
- ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Ya(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Zd(t,e){await wo(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:Lk(e)})}async function wr(t,e,n,r=void 0){return Gd(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var Bk=new Set;function hr(t){Bk.add(t),t.catch(()=>{}).finally(()=>Bk.delete(t))}var pt=t=>({response_type:"ephemeral",text:t}),AM="*/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._",_M=["claude","cursor","codex"];function Jd(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 Xd=(t,e)=>pt(`No live Track matches \u201C${Y(e)}\u201D. Live: ${t.map(n=>`\`${Y(n.name)}\``).join(" \xB7 ")||"none"}`);async function Fk(t,e,{name:n,repo:r,responseUrl:o}){let s=t.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=e.maxConcurrent){await Kt(o,{...pt(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Kt(o,{...pt("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?Ki.basename(c.base):"fresh workspace"),lead:"claude"},u=await Gd(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:Lk(l),unfurl_links:!1});l.ts=u.ts,(await Qd())[i.id]=l,await en(),await Kt(o,{...pt(`\u2713 *${Y(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Kt(o,{...pt(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function EM(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await tf(n.id);s&&(s.archivedAt=Date.now(),await en(),await Zd(e,s).catch(()=>{})),await Kt(r,{...pt(`\u2713 *${Y(n.name)}* archived.`),replace_original:!0})}catch(s){await Kt(r,{...pt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function IM(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Kt(o,{...pt(`\u2713 Handing *${Y(n.name)}* to ${Y(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Kt(o,{...pt(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function RM(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 Kt(r,{...pt(`\u2713 Stopped *${Y(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 Kt(r,{...pt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function $M(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Ds(`${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 *${Y(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function OM(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?Ki.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 *${Y(s.name)}* \u2014 ${Y(i)}${a.branch?` \`${Y(a.branch)}\``:""} \xB7 lead ${Y(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
412
+ `;await U.writeFile(H.join(t.dir,$s),o)}async function DC(t,e){let n=await W(),r=tt(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:Eo(e.hookToken),createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),dt(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]||{},...Vi(t)},await z())}async function BC(t,e=t.finishedAt){let n=await W(),r=tt(n,t.trackId);if(!r)return;let o=r.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=t.conflicts.length?"conflict":"merged",o.stat=t.stat),dt(r,"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:e?e-t.startedAt:null}),await z()}async function jC(t){let e=await W(),n=tt(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"),dt(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]||{},...Vi(t)},await z()}async function LC(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=H.join(e.cwd,r);await U.mkdir(H.dirname(o),{recursive:!0}),await U.copyFile(n,o)}catch{}return r}async function Zd(t){if(t.box&&await ye(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await re(e.leadDir,H.join(t.dir,e.name)).catch(()=>{});await U.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await et(t.leadBox).catch(()=>null))?.cwd;n&&await re(n,t.dir).catch(()=>{})}}async function FC(t,e){let n=(e.startedAt||Date.now())+IC;for(;;){if(e.canceled||e.forceMerge)return;try{await U.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Mn(RC)}}function Mn(t){return new Promise(e=>setTimeout(e,t))}var UC=t=>!!t&&!t.finishedAt&&!t.canceled&&!t.error;function zk({tracks:t=[],views:e=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of t)!o||o.archivedAt&&!o.archiving||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(UC)&&r.push(`merge-back:${o.id}`));return e.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}import Us from"node:fs/promises";import Yi from"node:path";import GC from"node:os";import HC from"ws";var Jk=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function zi(t,e,n={}){let r=await fetch(`${Jk()}/${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 Qd=(t,e)=>zi("chat.postMessage",t,e),go=(t,e)=>zi("chat.update",t,e);async function Vt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function WC(t,e,n={}){let r=await fetch(`${Jk()}/${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 Yk(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await WC("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 zi("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var qC=2200;function Xk({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 zi("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new HC(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=h=>{try{o.send(JSON.stringify(h?{envelope_id:f.envelope_id,payload:h}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},qC);Promise.resolve().then(()=>e(f)).then(h=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?h:void 0))},h=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${h?.message||h}`)})}),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 t0=()=>process.env.BOSTRAT_STATE_DIR||Yi.join(process.env.HOME||GC.tmpdir(),".bostrat"),of=()=>Yi.join(t0(),"slack.json"),Ji=null,tf=null,ef=Promise.resolve();async function af(){return Ji||(tf??=Us.readFile(of(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>Ji=t).finally(()=>tf=null),tf)}async function Zk(){let t=JSON.stringify({threads:Ji},null,2),e=`${of()}.${process.pid}.tmp`;await Us.mkdir(t0(),{recursive:!0}),await Us.writeFile(e,t),await Us.rename(e,of())}async function nn(){ef=ef.then(Zk,Zk),await ef.catch(()=>{})}async function cf(t){return(await af())[t]??null}async function VC(t,e){let n=await af();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var Q=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function Hs(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function zC(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Hs(n.replace(/\n+/g," "),e)}function e0(t){let e=`*${Q(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${Q(t.repoLabel)} \xB7 \`${Q(t.branch||"?")}\` \xB7 lead *${Q(t.lead)}*`,r=t.kickoff?`> ${Q(Hs(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
413
+ ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${ti(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function sf(t,e){await go(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:e0(e)})}async function gr(t,e,n,r=void 0){return Qd(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var Qk=new Set;function wr(t){Qk.add(t),t.catch(()=>{}).finally(()=>Qk.delete(t))}var ht=t=>({response_type:"ephemeral",text:t}),JC="*/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._",YC=["claude","cursor","codex"];function nf(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 rf=(t,e)=>ht(`No live Track matches \u201C${Q(e)}\u201D. Live: ${t.map(n=>`\`${Q(n.name)}\``).join(" \xB7 ")||"none"}`);async function n0(t,e,{name:n,repo:r,responseUrl:o}){let s=t.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=e.maxConcurrent){await Vt(o,{...ht(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Vt(o,{...ht("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?Yi.basename(c.base):"fresh workspace"),lead:"claude"},u=await Qd(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:e0(l),unfurl_links:!1});l.ts=u.ts,(await af())[i.id]=l,await nn(),await Vt(o,{...ht(`\u2713 *${Q(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Vt(o,{...ht(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function XC(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await cf(n.id);s&&(s.archivedAt=Date.now(),await nn(),await sf(e,s).catch(()=>{})),await Vt(r,{...ht(`\u2713 *${Q(n.name)}* archived.`),replace_original:!0})}catch(s){await Vt(r,{...ht(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function ZC(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Vt(o,{...ht(`\u2713 Handing *${Q(n.name)}* to ${Q(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Vt(o,{...ht(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function QC(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 Vt(r,{...ht(`\u2713 Stopped *${Q(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 Vt(r,{...ht(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function tM(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Hs(`${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 *${Q(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function eM(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?Yi.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 *${Q(s.name)}* \u2014 ${Q(i)}${a.branch?` \`${Q(a.branch)}\``:""} \xB7 lead ${Q(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
400
414
  `)+(o?`
401
- _${o} archived_`:"")}async function MM(t,e){let n=await tr();if(!n)return pt("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 pt("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(f=>f.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(hr(Fk(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),pt(`Starting *${Y(u)}* on ${Y(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:$M(u,i)}}case"archive":{if(!a.length)return pt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Jd(await r.listTracks(),a.join(" "));return i?(hr(EM(t,n,{track:i,responseUrl:e.response_url})),pt(`Archiving *${Y(i.name)}*\u2026`)):Xd(c,a.join(" "))}case"handoff":{let i=r.knownAgents??_M,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return pt(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=Jd(await r.listTracks(),l);return u?(hr(IM(t,n,{track:u,agent:c,responseUrl:e.response_url})),pt(`Handing *${Y(u.name)}* to ${Y(c)}\u2026`)):Xd(f,l)}case"stop":{if(!a.length)return pt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Jd(await r.listTracks(),a.join(" "));return i?(hr(RM(t,n,{track:i,responseUrl:e.response_url})),pt(`Stopping *${Y(i.name)}*\u2026`)):Xd(c,a.join(" "))}case"list":return pt(await OM(r));default:return pt(AM)}}async function CM(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await tf(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 en(),await wo(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(l||"question")}~
402
- \u2713 *${Y(a)}*`}}]}).catch(()=>{})}}catch(c){await Kt(n.response_url,pt(`Couldn't answer: ${c?.message||c}`))}}async function NM(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(d=>d.type==="pr.offered"&&d.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===s))throw new Error("offer already answered");let f=a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;f&&await i.sayToLead(c.id,gi(f,{branch:u.payload.branch,base:u.payload.base}),{kind:f.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:f?a:"dismiss",actions:f}})}catch(c){await Kt(n.response_url,pt(`Couldn't act on the offer: ${c?.message||c}`))}}async function PM(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await tr();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 Kt(e.response_url,{...pt(`Starting *${Y(s)}*\u2026`),replace_original:!0}),hr(Fk(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"?hr(CM(t,r,e,o)):n.action_id==="bostrat-ship"&&hr(NM(t,r,e,o))}async function DM(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 SM(e.channel,e.thread_ts);if(!n)return;let r=await tr();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await wr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function BM(t){return async e=>{if(e.type==="slash_commands")return MM(t,e.payload);if(e.type==="interactive"){await PM(t,e.payload);return}if(e.type==="events_api"){await DM(t,e.payload?.event);return}}}var jM=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 LM(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${Y(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:Ds(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 FM(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await wo(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await wr(t,e,r.question||"The lead needs an answer",LM(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await en()}async function UM(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 en(),await wo(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(s||"question")}~
403
- \u2713 _answered_`}}]})}async function HM(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 wr(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Y(Ds(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await en()}async function WM(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await en();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await wo(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function qM(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??KM)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Nk(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${Y(o.name)}*${a}${o.group?` (group \`${Y(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${Y(a.name)}*`).join(", ");await wr(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${Y(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function KM(t,e){let n=await Be(t);if(!n)return null;let r=ae(n,e);if(!r)return null;let o=await Ps.readFile(ee(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function GM(t,e){if(!jM.has(e.type))return;let n=await tf(e.trackId);if(!n)return;let r=await tr();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 en(),await Zd(r,n);return;case"turn.agent":{if(!o.text)return;let s=TM(o.text);if(!s)return;await wr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Y(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Ya(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return FM(r,n,e);case"prompt.answered":return UM(r,n,e);case"pr.offered":return HM(r,n,e);case"pr.offer.answered":return WM(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 wr(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 en(),await Zd(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await wr(r,n,`Live preview ${o.name}: ${Qa(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Y(o.name)}*: ${Qa(o.slug)}
404
- _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return qM(r,n,e,t.deps)}}function Uk(t){let e={deps:t};Ht.on("event",s=>{GM(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await tr()||(r&&clearInterval(r),r=null,n=Pk({getToken:async()=>(await tr())?.appToken,onEnvelope:BM(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import VM from"node:fs/promises";function zM(t){let e=new Map;for(let n of String(t||"").split(`
405
- `)){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 JM(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}var XM=new Set(["Write","Edit","NotebookEdit"]),YM=220,ZM=160;function QM(t,e){let n=t?.tool_name??null,r={tool:n},o=t?.tool_input;if(!o||typeof o!="object")return r;if(XM.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=e?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,YM)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Hn(o.command.trim().replace(/\s+/g," ")).slice(0,ZM));return r}function tC(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Ne(e.cwd,e.sessionId):e?.cwd?zc(e.cwd):null}async function Hk(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,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":{let c=String(t.prompt??t.user_prompt??""),l=Si(c);await i({type:"turn.user",actor:l?"peer":"user",payload:{promptHead:(l?l.text:c).slice(0,160)||null,...l?{peer:{socket:l.socket,name:l.name,mode:l.mode}}:{}}});break}case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:QM(t,o)});break;case"Stop":await eC(r,o,t,i);break;default:break}return a}async function eC(t,e,n,r){let o=[],s=tC(n,e);if(s)try{o=zM(await VM.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=JM(await B(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=f.model||"unknown",p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=f.tokens[m];p.messageIds.push(f.messageId),u.set(d,p)}for(let[f,d]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:gn(f,d.tokens),tokens:d.tokens,messageId:d.messageIds[d.messageIds.length-1],messageIds:d.messageIds,apiCalls:d.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 oC from"node:fs";import ef from"node:fs/promises";import sC from"node:http";import go from"node:path";import zi from"node:os";import{createRequire as aC}from"node:module";import nf from"ws";import nC from"node:path";import rC from"node:os";var Bs=process.env.BOSTRAT_STATE_DIR||nC.join(process.env.HOME||rC.tmpdir(),".bostrat");function Wk(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 qk(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 Gi=(()=>{try{return aC(import.meta.url)("../package.json").version||null}catch{return null}})(),iC=process.env.BOSTRAT_STATE_DIR||go.join(process.env.HOME||zi.tmpdir(),".bostrat");function cC(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 lC({home:t=process.env.HOME||zi.homedir(),env:e=process.env}={}){let n=r=>{try{return oC.statSync(r),!0}catch{return!1}};return{claude:n(go.join(t,".claude",".credentials.json")),codex:n(go.join(e.CODEX_HOME||go.join(t,".codex"),"auth.json"))}}var Rt=1,Kk=1e3,uC=3e4,dC=6e4,fC=1500,pC=500,Vi=100,mC=2e3,hC=25e3,wC=6e4,gC=6e4;function Gk({url:t,token:e,nodeId:n=zi.hostname(),name:r=zi.hostname(),listTracks:o,detectAgents:s=null,sampleMetrics:a=null,metricsMs:i=gC,selfUrl:c=`http://127.0.0.1:${process.env.PORT||3362}`,log:l=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=qk(t);let u=c.replace(/^http/,"ws"),f=new Map,d=new Map,p=go.join(iC,"plane-link","cursor.json"),m={},h={},y=new Map,k=null,v=!1,I=!1,R=Kk,z=null,O=null,M=null,S=null,N=null,D=!1;async function it(){try{m=JSON.parse(await ef.readFile(p,"utf8"))}catch{m={}}}function ct(){D=!0,!M&&(M=setTimeout(async()=>{if(M=null,!!D){D=!1;try{await ef.mkdir(go.dirname(p),{recursive:!0}),await ef.writeFile(p,JSON.stringify(m))}catch(G){l.error("[plane-link] cursor flush failed:",G?.message||G)}}},mC),M.unref?.())}function T(G){return!k||k.readyState!==nf.OPEN?!1:(k.send(JSON.stringify(G)),!0)}function g(G){let j=Date.now();for(let{trackId:b,event:_}of G){let lt=y.get(_.id);y.set(_.id,{trackId:b,seq:_.seq,event:_,sentAt:j,tries:(lt?.tries??0)+1})}return T({v:Rt,ch:"events",type:"append",events:G})}function w(G){let j=null;for(let b of y.values())b.trackId===G&&(j==null||b.seq<j)&&(j=b.seq);return j}function A(G){let j=h[G];if(j==null)return;let b=w(G),_=b==null?j:Math.min(j,b-1);_>(m[G]??0)&&(m[G]=_,ct())}function E(){if(!v)return;let G=Date.now(),j=[...y.values()].filter(b=>b.event&&G-b.sentAt>=wC);if(j.length){j.sort((b,_)=>b.trackId===_.trackId?b.seq-_.seq:b.trackId<_.trackId?-1:1),l.log(`[plane-link] re-sending ${j.length} unacked event(s) the plane never confirmed`);for(let b=0;b<j.length;b+=Vi){let _=j.slice(b,b+Vi).map(lt=>({trackId:lt.trackId,event:lt.event}));if(!g(_))return}}}async function J(){if(!a)return;let G;try{G=await a()}catch(j){l.error("[plane-link] metrics sample failed:",j?.message||j);return}G&&T({v:Rt,ch:"ctl",type:"metrics",metrics:G})}function W(){a&&(N&&clearInterval(N),J(),N=setInterval(J,i),N.unref?.())}async function at(){let G;try{G=await o()}catch(j){l.error("[plane-link] backfill listTracks failed:",j?.message||j);return}T({v:Rt,ch:"ctl",type:"tracks",ids:G.map(j=>j.id).filter(j=>typeof j=="string")});for(let j of G){if(!v)return;let b;try{b=await B(j)}catch{continue}let _=m[j.id]??0,lt=b.filter(nt=>nt.seq>_&&typeof nt.id=="string");for(let nt=0;nt<lt.length;nt+=Vi){let bt=lt.slice(nt,nt+Vi).map(jt=>({trackId:j.id,event:jt}));if(!g(bt))return}}}function Nt(G){!v||!G?.trackId||typeof G.id!="string"||g([{trackId:G.trackId,event:G}])}let Tt=new Map;async function Xs(G){let{reqId:j,method:b,path:_,headers:lt={},body:nt}=G,bt=Object.fromEntries(Object.entries(lt).filter(([,pe])=>pe!=null)),jt=new AbortController;Tt.set(j,jt);try{if(G.host){await gr(G,bt,jt);return}let pe=await fetch(c+_,{method:b,headers:bt,body:nt?Buffer.from(nt,"base64"):void 0,signal:jt.signal}),rn=Object.fromEntries(pe.headers);for(let Zt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete rn[Zt];if(rn["content-type"]||(rn["content-type"]="application/octet-stream"),T({v:Rt,ch:"control",type:"http_head",reqId:j,status:pe.status,headers:rn}),pe.body){for await(let Zt of pe.body)if(!T({v:Rt,ch:"control",type:"http_data",reqId:j,data:Buffer.from(Zt).toString("base64")}))break}T({v:Rt,ch:"control",type:"http_end",reqId:j,error:null})}finally{Tt.delete(j)}}function gr(G,j,b){let{reqId:_,method:lt,path:nt,body:bt}=G,jt=new URL(c);return new Promise(pe=>{let rn=sC.request({host:jt.hostname,port:jt.port,method:lt,path:nt,headers:{...j,host:G.host},signal:b.signal},Zt=>{let{connection:lD,"keep-alive":uD,"transfer-encoding":dD,upgrade:fD,...Mx}=Zt.headers;T({v:Rt,ch:"control",type:"http_head",reqId:_,status:Zt.statusCode||502,headers:Mx}),Zt.on("data",Cx=>{T({v:Rt,ch:"control",type:"http_data",reqId:_,data:Buffer.from(Cx).toString("base64")})||Zt.destroy()}),Zt.on("end",()=>{T({v:Rt,ch:"control",type:"http_end",reqId:_,error:null}),pe()}),Zt.on("error",()=>{T({v:Rt,ch:"control",type:"http_end",reqId:_,error:"view upstream error"}),pe()})});rn.on("error",Zt=>{T({v:Rt,ch:"control",type:"http_end",reqId:_,error:Zt?.message||"view request failed"}),pe()}),bt&&rn.write(Buffer.from(bt,"base64")),rn.end()})}function Ys(){if(I)return;k=new nf(t,{headers:{authorization:`Bearer ${e}`}}),k.on("open",async()=>{let j=null;try{j=await s?.()||null}catch{j=null}T({v:Rt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Gi,providers:lC(),...j?{agents:j}:{}})}),k.on("message",j=>{let b;try{b=JSON.parse(j)}catch{return}if(b.ch==="ctl"&&b.type==="hello_ok"){v=!0,R=Kk,z=null,l.log(`[plane-link] connected to plane as ${n}`),b.minNodeVersion&&Gi&&cC(Gi,b.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${Gi}; the plane asks for \u2265${b.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),S&&clearInterval(S),S=setInterval(()=>{T({v:Rt,ch:"ctl",type:"ping"}),E()},hC),S.unref?.(),W(),at().catch(_=>l.error("[plane-link] backfill failed:",_?.message||_));return}if(b.ch==="ctl"&&b.type==="usage_caps"){pi(b.caps??{},{source:"plane",updatedAt:b.updatedAt??Date.now()}).then(_=>l.log(`[plane-link] spend caps ${Object.keys(_.caps).length?`updated (${Object.keys(_.caps).join(", ")})`:"cleared"} from plane`)).catch(_=>l.error("[plane-link] spend-caps push rejected:",_?.message||_));return}if(b.ch==="events"&&b.type==="ack"&&Array.isArray(b.ids)){let _=new Set;for(let lt of b.ids){let nt=y.get(lt);nt&&(y.delete(lt),(h[nt.trackId]??0)<nt.seq&&(h[nt.trackId]=nt.seq),_.add(nt.trackId))}for(let lt of _)A(lt);return}if(b.ch==="control"&&b.type==="http_open"){Xs(b).catch(_=>T({v:Rt,ch:"control",type:"http_end",reqId:b.reqId,error:_?.message||"tunnel error"}));return}if(b.ch==="control"&&b.type==="http_cancel"){Tt.get(b.reqId)?.abort();return}if(b.ch==="attach"&&b.type==="open"){let _=new nf(u+b.path,Array.isArray(b.protocols)?b.protocols:[],b.host?{headers:{host:b.host}}:void 0);f.set(b.streamId,_),d.set(b.streamId,[]),_.on("open",()=>{let nt=d.get(b.streamId)||[];d.delete(b.streamId);for(let{buf:bt,bin:jt}of nt)_.send(bt,{binary:jt})}),_.on("message",(nt,bt)=>{let jt=bt?nt:Buffer.from(String(nt));T({v:Rt,ch:"attach",type:"data",streamId:b.streamId,data:jt.toString("base64"),...bt?{bin:!0}:{}})});let lt=()=>{d.delete(b.streamId),f.delete(b.streamId)&&T({v:Rt,ch:"attach",type:"close",streamId:b.streamId})};_.on("close",lt),_.on("error",lt);return}if(b.ch==="attach"&&b.type==="data"){let _=f.get(b.streamId);if(!_)return;let lt=Buffer.from(b.data,"base64"),nt=b.bin===!0,bt=d.get(b.streamId);bt?bt.push({buf:lt,bin:nt}):_.send(lt,{binary:nt});return}if(b.ch==="attach"&&b.type==="close"){let _=f.get(b.streamId);f.delete(b.streamId),d.delete(b.streamId),_?.close();return}});let G=()=>{v&&l.log("[plane-link] disconnected from plane"),v=!1,S&&(clearInterval(S),S=null),N&&(clearInterval(N),N=null),y.clear(),h={};for(let _ of f.values())_.close();f.clear(),d.clear();for(let _ of Tt.values())_.abort();if(Tt.clear(),I||O)return;z==null&&(z=Date.now());let j=Date.now()-z<dC,b=j?fC+Math.random()*pC:R;O=setTimeout(()=>{O=null,Ys()},b),O.unref?.(),j||(R=Math.min(R*2,uC))};k.on("close",G),k.on("error",G)}return Ht.on("event",Nt),it().then(Ys),{stop:()=>{I=!0,Ht.off("event",Nt),O&&clearTimeout(O),M&&clearTimeout(M),S&&clearInterval(S),N&&clearInterval(N),T({v:Rt,ch:"ctl",type:"bye",reason:"shutdown"}),k?.close()},status:()=>({connected:v,nodeId:n,cursor:{...m},inflight:y.size})}}import yC from"node:fs";import Vk from"node:fs/promises";import js from"node:os";import bC from"node:path";var kC=1024*1024,Ji=t=>Math.round(t/kC);async function xC(t){try{let e=await Vk.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:Ji(r),diskTotalMb:Ji(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function vC(){try{return(await Ce()).filter(e=>e.isBostrat).length}catch{return null}}async function SC(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await Vk.readdir(bC.join(Bs,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function zk({stateDir:t=Bs,now:e=Date.now}={}){let n=js.totalmem(),[r,o,s,a]=await Promise.all([Ba().catch(()=>null),xC(yC.existsSync(t)?t:js.tmpdir()),vC(),SC()]),i=js.cpus()?.length||null,c=js.loadavg()?.[0]??null;return{ts:e(),loadavg1:c!=null?Math.round(c*100)/100:null,cpus:i,cpuPct:c!=null&&i?Math.min(100,Math.round(c/i*100)):null,memUsedMb:r!=null?Ji(n-r):null,memTotalMb:Ji(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(js.uptime())}}function rf(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 Jk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Xk=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Xi=t=>Math.round(t*1e4)/1e4;async function Yk(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=Jk(),f=0,d=[];for(let p of t||[]){let m=await B(p),h=ye(m,{boxLabels:rf(p)});if(h.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.asRecordedUsd||0,c+=h.unpricedTurns||0;for(let y of h.unpricedModels||[])l.add(y);Xk(u,h.totalTokens);for(let[y,k]of Object.entries(h.byModel))n[y]||={usd:0,events:0,tokens:Jk()},n[y].usd=Xi(n[y].usd+k.usd),n[y].events+=k.events,Xk(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(h.byProvider))r[y]||={usd:0,events:0},r[y].usd=Xi(r[y].usd+k.usd),r[y].events+=k.events;e.push({id:p.id,name:p.name,usd:h.totalUsd,rollup:h})}return e.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Xi(o),asRecordedUsd:Xi(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:e}}var Cn=t=>`$${t.toFixed(4)}`,yo=(t,e)=>String(t).padEnd(e);function Zk(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 ${Cn(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) priced at current table rates (differs from as-recorded)`),e.asRecordedUsd!=null&&Math.abs(e.asRecordedUsd-e.totalUsd)>=.005&&r.push(`as-recorded total ${Cn(e.asRecordedUsd)} \xB7 current-rates total ${Cn(e.totalUsd)} (${e.totalUsd>=e.asRecordedUsd?"+":""}${Cn(e.totalUsd-e.asRecordedUsd)})`),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(` ${yo(a,26)} ${yo(Cn(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(` ${yo(a.name||a.id,30)} ${Cn(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${yo(c.label,40)} ${yo(Cn(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((f,d)=>Number(f[0])-Number(d[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${yo(Cn(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
406
- `)}import IC from"node:fs/promises";import RC from"node:path";var Yi=t=>t.payload?.ts??t.ts,TC=12e4,Nn=t=>Math.round(t*1e4)/1e4,AC=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function _C(t,{since:e}){let{track:n,events:r,brief:o}=t,s=v=>Yi(v)>=e,a=Gn([Uo(n?.name),Uo(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let v of r)if(v.type==="subtask.delegated"&&v.boxId&&v.payload?.task){let I=Gn(v.payload.task);I&&i.set(v.boxId,I)}let c=new Map,l=v=>{let I=v||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},u=v=>({textCat:v,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((v,I)=>Yi(v)-Yi(I)),d=0,p=0,m=0,h={turns:0};for(let v of f){let I=l(v.boxId),R=I.segments[I.segments.length-1],z=Yi(v);switch(I.lastTs!=null&&z>I.lastTs&&s(v)&&(R.activeMs+=Math.min(z-I.lastTs,TC)),I.lastTs=z,v.provider&&(I.provider=v.provider),v.type){case"turn.user":{let O=v.payload?.text;if(v.source==="hook"||!String(O||"").trim())break;let M=u(Gn(O));I.segments.push(M),s(v)&&(M.turns+=1);break}case"peer.message":{let O=v.payload?.text;if(!String(O||"").trim())break;let M=u(Gn(O));I.segments.push(M),s(v)&&(M.turns+=1);break}case"tool.called":{if(v.source==="transcript")break;let O=v.payload?.tool;O&&(R.tools[O]=(R.tools[O]||0)+1),s(v)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(v))break;let{usd:O,tokens:M,model:S,unpriced:N}=De(v);N&&(h.turns+=1),R.usd+=O,M&&(R.tokens.input+=M.input,R.tokens.output+=M.output,R.tokens.cacheRead+=M.cacheRead,R.tokens.cacheWrite+=M.cacheWrite),R.models[S]=(R.models[S]||0)+O,v.provider&&(R.provider=v.provider);break}case"handoff.done":s(v)&&(d+=1);break;case"subtask.merged":s(v)&&(p+=1);break;case"error":s(v)&&v.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,k=!1;for(let[v,I]of c)for(let R of I.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let O=R.textCat?.category??i.get(v)?.category??Yp(R.tools)?.category??a?.category??"uncategorized";y.has(O)||y.set(O,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let M=y.get(O);M.usd+=R.usd,M.activeMs+=R.activeMs,M.turns+=R.turns,M.toolCalls+=R.toolCalls;for(let[N,D]of Object.entries(R.models))M.models[N]=(M.models[N]||0)+D;let S=R.provider||I.provider;S&&(M.providers[S]=(M.providers[S]||0)+R.usd),S==="cursor"&&R.usd===0&&(k=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:k,unpricedTurns:h.turns}}function t0(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 h of t||[]){let{track:y,events:k}=h;if(!k?.length){a+=1;continue}let{perCategory:v,handoffs:I,merged:R,failed:z,sawCursorActivity:O,unpricedTurns:M}=_C(h,{since:r});O&&(i=!0),c+=M;let S={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:I,subtasksMerged:R,subtasksFailed:z};for(let[N,D]of v){S.usd+=D.usd,S.activeMs+=D.activeMs,S.turns+=D.turns,S.categories[N]={usd:Nn(D.usd),activeMs:D.activeMs};for(let[T,g]of Object.entries(D.models))S.models[T]=Nn((S.models[T]||0)+g);for(let[T,g]of Object.entries(D.providers))S.providers[T]=Nn((S.providers[T]||0)+g);o.has(N)||o.set(N,AC());let it=o.get(N);it.usd+=D.usd,it.activeMs+=D.activeMs,it.turns+=D.turns,it.toolCalls+=D.toolCalls;for(let[T,g]of Object.entries(D.models))it.models[T]=(it.models[T]||0)+g;for(let[T,g]of Object.entries(D.providers))it.providers[T]=(it.providers[T]||0)+g;let ct=it.byTrack.get(y?.id)||{usd:0,activeMs:0};ct.usd+=D.usd,ct.activeMs+=D.activeMs,it.byTrack.set(y?.id,ct)}(S.usd>0||S.activeMs>0||S.turns>0)&&(S.usd=Nn(S.usd),S.dominantCategory=EC(S.categories),s.push(S))}let l=[...o.values()].reduce((h,y)=>h+y.usd,0),u=[...o.values()].reduce((h,y)=>h+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((t||[]).map(h=>[h.track?.id,h.track?.name||h.track?.id])),m=[...o.entries()].map(([h,y])=>({category:h,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:Nn(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:Qk(y.models),providers:Qk(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((k,v)=>v[1].usd-k[1].usd||v[1].activeMs-k[1].activeMs).slice(0,2).map(([k,v])=>({trackId:k,name:p.get(k),usd:Nn(v.usd),activeMs:v.activeMs}))})).sort((h,y)=>y.usd-h.usd||y.activeMs-h.activeMs);return s.sort((h,y)=>y.usd-h.usd||y.activeMs-h.activeMs),{since:r,capturedAt:n,totalUsd:Nn(l),activeMs:u,turns:[...o.values()].reduce((h,y)=>h+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var EC=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},Qk=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Nn(n)]));var bo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function $C(t){let e=String(t||"7d");return e in bo?{key:e,ms:bo[e]}:{key:"7d",ms:bo["7d"]}}function OC(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=wi(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 MC(t){if(!t?.cwd)return null;try{let n=(await IC.readFile(RC.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 e0(t,{window:e="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:$C(e),f=[];for(let d of t||[]){let p=await B(d),m=OC(d,p);i&&c<o&&(m=m.filter(h=>(h.ts??0)<=c)),f.push({track:d,events:m,brief:await MC(d)})}return{window:l,...t0(f,{windowMs:u,now:c})}}var Pn=36e5,s0=24*Pn,CC=400,NC=12,ko=t=>Math.round(t*1e4)/1e4;function PC({window:t,from:e,to:n,now:r=Date.now()}={}){let o=Number(e),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(t||"7d")in bo?String(t||"7d"):"7d",i=bo[a];return{key:a,since:i==null?null:r-i,until:r}}var DC=(t,e)=>t!=null&&e-t<=48*Pn?Pn:s0;function n0(t,e){if(e===Pn)return Math.floor(t/Pn)*Pn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function BC(t,e){if(e===Pn)return t+Pn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var r0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),o0=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},jC=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,ko(n)]));async function a0(t,e={}){let n=e.now??Date.now(),r=PC({...e,now:n}),o=r.since==null?s0:DC(r.since,r.until),s=S=>(r.since==null||S>=r.since)&&S<r.until,a=r.since!=null?r.until-r.since:null,i=S=>a!=null&&S>=r.since-a&&S<r.since,c={},l={},u=r0(),f=0,d=0,p=0,m=0,h=0,y=new Set,k=new Map,v=[],I=[],R=[],z=null;for(let S of t||[]){let D=(await B(S)).filter(T=>T.type==="cost.recorded");if(!D.length){S.archivedAt||R.push(S.name||S.id);continue}let it=[];for(let T of D){let g=T.ts??0;s(g)?it.push(T):i(g)&&(h+=De(T).usd)}if(!it.length)continue;let ct=ye(it,{boxLabels:rf(S)});f+=ct.totalUsd,d+=ct.turns,p+=ct.repricedTurns||0,m+=ct.unpricedTurns||0;for(let T of ct.unpricedModels||[])y.add(T);o0(u,ct.totalTokens);for(let[T,g]of Object.entries(ct.byModel))c[T]||={usd:0,events:0,tokens:r0()},c[T].usd=ko(c[T].usd+g.usd),c[T].events+=g.events,o0(c[T].tokens,g.tokens);for(let[T,g]of Object.entries(ct.byProvider))l[T]||={usd:0,events:0},l[T].usd=ko(l[T].usd+g.usd),l[T].events+=g.events;for(let T of it){let{usd:g}=De(T);if(!g)continue;let w=n0(T.ts??0,o),A=k.get(w)??{t:w,total:0,byProvider:{}};A.total+=g;let E=T.provider||"unknown";A.byProvider[E]=(A.byProvider[E]||0)+g,k.set(w,A),(z==null||w<z)&&(z=w)}v.push({id:S.id,name:S.name||S.id,archived:!!S.archivedAt,usd:ct.totalUsd,turns:ct.turns,providers:Object.keys(ct.byProvider)});for(let[T,g]of Object.entries(ct.byBox))I.push({trackId:S.id,trackName:S.name||S.id,boxId:T,...g})}let O=r.since!=null?n0(r.since,o):z,M=[];for(let S=O,N=0;S!=null&&S<r.until&&N<CC;S=BC(S,o),N++){let D=k.get(S);M.push(D?{t:S,total:ko(D.total),byProvider:jC(D.byProvider)}:{t:S,total:0,byProvider:{}})}return v.sort((S,N)=>N.usd-S.usd),I.sort((S,N)=>N.usd-S.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:M,fleet:{totalUsd:ko(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:v.length,tracksSilentLive:R},prior:a!=null?{totalUsd:ko(h),since:r.since-a,until:r.since}:null,tracks:v.slice(0,50),boxes:I.slice(0,NC)}}import xo from"node:fs/promises";import LC from"node:fs";import Ct from"node:path";import FC from"node:os";import{execFile as UC}from"node:child_process";import{promisify as HC}from"node:util";var WC=HC(UC),i0=process.env.BOSTRAT_STATE_DIR||Ct.join(process.env.HOME||FC.tmpdir(),".bostrat"),Zi=!1,of=null;async function sf(t,e){let n=await xo.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await xo.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await xo.mkdir(Ct.dirname(e),{recursive:!0}),await xo.copyFile(t,e),!0)}var qC=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function Qi(t,e,{skip:n=null}={}){let r=0,o=await xo.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(qC.has(s.name)||n?.has(s.name))continue;let a=Ct.join(t,s.name);s.isDirectory()?r+=await Qi(a,Ct.join(e,s.name)):s.isFile()&&(r+=await sf(a,Ct.join(e,s.name))?1:0)}return r}async function c0({stateDir:t=i0,dest:e=process.env.BOSTRAT_SYNC_DIR||`${i0}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await xo.mkdir(e,{recursive:!0});let o=0;o+=await sf(Ct.join(t,"tracks.json"),Ct.join(e,"tracks.json"))?1:0,o+=await sf(Ct.join(t,"events.jsonl"),Ct.join(e,"events.jsonl"))?1:0,o+=await Qi(Ct.join(t,"events"),Ct.join(e,"events")),o+=await Qi(Ct.join(t,"memory"),Ct.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Qi(Ct.join(u.cwd,".bostrat"),Ct.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>WC("git",["-C",e,...u]);LC.existsSync(Ct.join(e,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Tc(e).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||Zi))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Zi&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Zi=!1,of=null}catch(u){Zi=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
407
- `).slice(-3).join(" | ").slice(0,300);f!==of&&(of=f,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${f}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as KC}from"node:child_process";import{promisify as GC}from"node:util";var VC=GC(KC),zC=12e4,JC=6e4;function XC(t){return`refs/bostrat/wip/${t}`}function d0(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function YC(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:zC}var tc=(t,e,n=15e3)=>VC("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),l0=new Map,u0=new Set;async function ZC(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await tc(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await tc(r,["remote","get-url","origin"])}catch{return u0.has(r)||(u0.add(r),e.log?.(`[wip-sync] ${n}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let o=await ms(r,"bostrat: wip snapshot"),{stdout:s}=await tc(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(l0.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=XC(n);return await tc(r,["push","--force","origin",`${o}:${i}`],JC),l0.set(r,a),await $({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
408
- `)[0]}`),{pushed:!1,reason:"push-failed"}}}function f0({listTracks:t,sweepMs:e=YC(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await t()??[];for(let i of a)i?.archivedAt||i?.kind==="setup"||await ZC(i,{log:n})}catch(a){n.log?.(`[wip-sync] sweep failed: ${a?.message||a}`)}finally{r=!1}}},s=setInterval(o,e);return s.unref?.(),o(),{stop:()=>clearInterval(s),cycle:o}}import{execFile as QC}from"node:child_process";import{promisify as tN}from"node:util";import{randomUUID as eN}from"node:crypto";import nN from"node:fs";import rN from"node:os";import oN from"node:path";var Bn=tN(QC),ec=t=>new Promise(e=>setTimeout(e,t)),jn=Uc,af=process.env.BOSTRAT_USAGE_PROBE_CWD||oN.join(rN.homedir(),".bostrat","usage-probe"),sN=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,aN=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,iN=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,p0=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Dn=null,Ls=null,Fs=null;async function cN(){try{return await Bn(C,P("has-session","-t",jn)),!0}catch{return!1}}function lN(){Dn&&clearTimeout(Dn),Dn=setTimeout(()=>uN().catch(()=>{}),sN),Dn.unref&&Dn.unref()}async function uN(){Dn&&(clearTimeout(Dn),Dn=null);try{await Bn(C,P("kill-session","-t",jn))}catch{}}async function dN(){let t=Date.now()+iN;for(;;){let e=await cf().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await ec(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await ec(500)}}async function fN(){if(await cN())return;if(!await un())throw new Error("host tmux server not running \u2014 start globo-tmux.service");nN.mkdirSync(af,{recursive:!0}),await on(af);let t=eN();await Bn(C,P("new-session","-d","-s",jn,"-c",af,"-x","200","-y","60",...Fc()));let e=`${Lc} --session-id ${t}`;await Bn(C,P("send-keys","-t",jn,e,"Enter")),await dN()}async function cf(){let{stdout:t}=await Bn(C,P("capture-pane","-t",jn,"-p"));return t}async function pN(){await fN(),await Bn(C,P("send-keys","-t",jn,"-l","/usage")),await Bn(C,P("send-keys","-t",jn,"Enter"));let t=Date.now()+aN,e="";for(;await ec(400),e=await cf(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await ec(900),e=await cf(),await Bn(C,P("send-keys","-t",jn,"Escape")).catch(()=>{}),lN(),e}function mN(t){let e=String(t||"").split(`
409
- `).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 hN(t){let e=String(t||"").split(`
410
- `).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 m0({fresh:t=!1}={}){return!t&&Ls&&Date.now()-Ls.at<p0?Ls.data:Fs||(Fs=(async()=>{try{let e=await pN(),n=mN(e),r={...n,contributors:hN(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ls={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 Ls={at:Date.now()-(p0-3e3),data:n},n}finally{Fs=null}})(),Fs)}var wN=262144;function gN(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 h0(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:gN(t.resets_in_seconds)}:null}function yN(t){let e=String(t||"").split(`
411
- `),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:h0(i.primary),week:h0(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 lf(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Vn(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 Ut(n.file,wN),o=yN(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 w0(t,{fresh:e=!1}={}){let n=new Set;for(let o of t||[])for(let s of o.boxes||[])s.agent&&n.add(s.agent);let r=[];if((n.has("claude")||n.size===0)&&r.push(await nc({agent:"claude"},{fresh:e})),n.has("codex")){let o=[],s=[...t||[]].sort((i,c)=>(i.archivedAt?1:0)-(c.archivedAt?1:0)||(c.createdAt||0)-(i.createdAt||0));for(let i of s)for(let c of i.boxes||[])c.agent==="codex"&&o.push({...c,cwd:c.cwd||i.cwd});let a=null;for(let i of o.slice(0,8)){let c=await lf(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await lf(null))}return n.has("cursor")&&r.push(await nc({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function nc(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?lf(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await m0({fresh:e})}}function bN(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var kN=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),rc=(t,e)=>Object.assign(new Error(t),{status:e});async function g0(t,e){if(!bN(e))throw rc("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Qt(e)}${t}`,{headers:kN(e)})}catch(o){throw rc(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw rc(r?.error||`plane replied ${n.status}`,n.status);return r}async function y0(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await g0(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function b0(t,{env:e=process.env}={}){if(!t)throw rc("message id required",400);return(await g0(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function uf(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 xN(t){let[e,n]=t.split("/"),r=uf(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 k0(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(xN).filter(Boolean)}function vN(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function x0(t){let e=k0(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:vN,allowed(r){if(n)return!0;let o=uf((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function v0(t,e){let n=k0(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=uf(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import S0 from"node:crypto";import oc from"node:fs";import SN from"node:path";function T0(t=process.env,{stateDir:e=Bs,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=SN.join(e,"api-token");try{let a=oc.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=S0.randomBytes(32).toString("base64url");try{oc.mkdirSync(e,{recursive:!0}),oc.writeFileSync(o,s+`
412
- `,{mode:384}),oc.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Us(t){let e=t?.socket?.remoteAddress||"";if(!e)return!1;let n=e.replace(/^::ffff:/,"");return n==="::1"||n==="127.0.0.1"||/^127\./.test(n)}function A0(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:S0.timingSafeEqual(n,r)}function _0(t,e=null){let n=t?.headers?.["x-bostrat-token"];if(typeof n=="string"&&n.trim())return n.trim();let r=t?.headers?.authorization||"",o=/^Bearer\s+(.+)$/i.exec(r)?.[1];if(o)return o.trim();try{return(e??new URL(t.url,"http://node")).searchParams.get("token")||""}catch{return""}}var TN=[/^\/api\/github\/token\b/],AN=t=>TN.some(e=>e.test(t));function E0({token:t,enforce:e=!0,log:n=console}={}){return function(o,s,a){let i=o.path||o.url||"";if(!i.startsWith("/api"))return a();let c=_0(o);return A0(c,t)?a():AN(i)?(n.log?.(`[perimeter] refused ${o.method} ${i} \u2014 sensitive route requires the node API token`),s.status(401).json({error:"this route requires the node API token"})):!e||Us(o)?a():(n.log?.(`[perimeter] refused ${o.method} ${i} from ${o.socket?.remoteAddress||"?"} \u2014 no node API token`),s.status(401).json({error:"unauthorized"}))}}function I0(t,e,{enforce:n=!0}={}){return!n||A0(_0(t),e)?!0:Us(t)}import _N from"node:http";import EN from"node:net";function R0(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 sc(t){let e=R0(t.headers.host);return e?{slug:e,view:Wl(e)}:null}var IN=t=>`<!doctype html><meta charset="utf-8">
415
+ _${o} archived_`:"")}async function nM(t,e){let n=await er();if(!n)return ht("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 ht("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(f=>f.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(wr(n0(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),ht(`Starting *${Q(u)}* on ${Q(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:tM(u,i)}}case"archive":{if(!a.length)return ht("Usage: `/bostrat archive <track>`");let{match:i,live:c}=nf(await r.listTracks(),a.join(" "));return i?(wr(XC(t,n,{track:i,responseUrl:e.response_url})),ht(`Archiving *${Q(i.name)}*\u2026`)):rf(c,a.join(" "))}case"handoff":{let i=r.knownAgents??YC,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return ht(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=nf(await r.listTracks(),l);return u?(wr(ZC(t,n,{track:u,agent:c,responseUrl:e.response_url})),ht(`Handing *${Q(u.name)}* to ${Q(c)}\u2026`)):rf(f,l)}case"stop":{if(!a.length)return ht("Usage: `/bostrat stop <track>`");let{match:i,live:c}=nf(await r.listTracks(),a.join(" "));return i?(wr(QC(t,n,{track:i,responseUrl:e.response_url})),ht(`Stopping *${Q(i.name)}*\u2026`)):rf(c,a.join(" "))}case"list":return ht(await eM(r));default:return ht(JC)}}async function rM(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await cf(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 nn(),await go(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(l||"question")}~
416
+ \u2713 *${Q(a)}*`}}]}).catch(()=>{})}}catch(c){await Vt(n.response_url,ht(`Couldn't answer: ${c?.message||c}`))}}async function oM(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(d=>d.type==="pr.offered"&&d.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===s))throw new Error("offer already answered");let f=a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;f&&await i.sayToLead(c.id,xi(f,{branch:u.payload.branch,base:u.payload.base}),{kind:f.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:f?a:"dismiss",actions:f}})}catch(c){await Vt(n.response_url,ht(`Couldn't act on the offer: ${c?.message||c}`))}}async function sM(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await er();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 Vt(e.response_url,{...ht(`Starting *${Q(s)}*\u2026`),replace_original:!0}),wr(n0(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"?wr(rM(t,r,e,o)):n.action_id==="bostrat-ship"&&wr(oM(t,r,e,o))}async function aM(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 VC(e.channel,e.thread_ts);if(!n)return;let r=await er();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await gr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function iM(t){return async e=>{if(e.type==="slash_commands")return nM(t,e.payload);if(e.type==="interactive"){await sM(t,e.payload);return}if(e.type==="events_api"){await aM(t,e.payload?.event);return}}}var cM=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 lM(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${Q(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:Hs(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 uM(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await go(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await gr(t,e,r.question||"The lead needs an answer",lM(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await nn()}async function dM(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 nn(),await go(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(s||"question")}~
417
+ \u2713 _answered_`}}]})}async function fM(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 gr(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Q(Hs(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await nn()}async function pM(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await nn();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await go(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function mM(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??hM)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Yk(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${Q(o.name)}*${a}${o.group?` (group \`${Q(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${Q(a.name)}*`).join(", ");await gr(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${Q(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function hM(t,e){let n=await Le(t);if(!n)return null;let r=ce(n,e);if(!r)return null;let o=await Us.readFile(ne(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function wM(t,e){if(!cM.has(e.type))return;let n=await cf(e.trackId);if(!n)return;let r=await er();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 nn(),await sf(r,n);return;case"turn.agent":{if(!o.text)return;let s=zC(o.text);if(!s)return;await gr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Q(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${ti(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return uM(r,n,e);case"prompt.answered":return dM(r,n,e);case"pr.offered":return fM(r,n,e);case"pr.offer.answered":return pM(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 gr(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 nn(),await sf(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await gr(r,n,`Live preview ${o.name}: ${ni(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Q(o.name)}*: ${ni(o.slug)}
418
+ _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return mM(r,n,e,t.deps)}}function r0(t){let e={deps:t};qt.on("event",s=>{wM(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await er()||(r&&clearInterval(r),r=null,n=Xk({getToken:async()=>(await er())?.appToken,onEnvelope:iM(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import gM from"node:fs/promises";function yM(t){let e=new Map;for(let n of String(t||"").split(`
419
+ `)){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 bM(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}var kM=new Set(["Write","Edit","NotebookEdit"]),xM=220,SM=160;function vM(t,e){let n=t?.tool_name??null,r={tool:n},o=t?.tool_input;if(!o||typeof o!="object")return r;if(kM.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=e?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,xM)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Wn(o.command.trim().replace(/\s+/g," ")).slice(0,SM));return r}function TM(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?De(e.cwd,e.sessionId):e?.cwd?Qc(e.cwd):null}async function o0(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 I(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":{let c=String(t.prompt??t.user_prompt??""),l=Ei(c);await i({type:"turn.user",actor:l?"peer":"user",payload:{promptHead:(l?l.text:c).slice(0,160)||null,...l?{peer:{socket:l.socket,name:l.name,mode:l.mode}}:{}}});break}case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:vM(t,o)});break;case"Stop":await AM(r,o,t,i);break;default:break}return a}async function AM(t,e,n,r){let o=[],s=TM(n,e);if(s)try{o=yM(await gM.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=bM(await P(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=f.model||"unknown",p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=f.tokens[m];p.messageIds.push(f.messageId),u.set(d,p)}for(let[f,d]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:yn(f,d.tokens),tokens:d.tokens,messageId:d.messageIds[d.messageIds.length-1],messageIds:d.messageIds,apiCalls:d.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 IM from"node:fs";import lf from"node:fs/promises";import RM from"node:http";import yo from"node:path";import Qi from"node:os";import{createRequire as $M}from"node:module";import uf from"ws";import _M from"node:path";import EM from"node:os";var Ws=process.env.BOSTRAT_STATE_DIR||_M.join(process.env.HOME||EM.tmpdir(),".bostrat");function s0(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 a0(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 Xi=(()=>{try{return $M(import.meta.url)("../package.json").version||null}catch{return null}})(),OM=process.env.BOSTRAT_STATE_DIR||yo.join(process.env.HOME||Qi.tmpdir(),".bostrat");function CM(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 MM({home:t=process.env.HOME||Qi.homedir(),env:e=process.env}={}){let n=r=>{try{return IM.statSync(r),!0}catch{return!1}};return{claude:n(yo.join(t,".claude",".credentials.json")),codex:n(yo.join(e.CODEX_HOME||yo.join(t,".codex"),"auth.json"))}}var Ct=1,i0=1e3,NM=3e4,PM=6e4,DM=1500,BM=500,Zi=100,jM=2e3,LM=25e3,FM=6e4,UM=6e4;function c0({url:t,token:e,nodeId:n=Qi.hostname(),name:r=Qi.hostname(),listTracks:o,detectAgents:s=null,sampleMetrics:a=null,metricsMs:i=UM,selfUrl:c=`http://127.0.0.1:${process.env.PORT||3362}`,log:l=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=a0(t);let u=c.replace(/^http/,"ws"),f=new Map,d=new Map,p=yo.join(OM,"plane-link","cursor.json"),m={},h={},g=new Map,k=null,S=!1,R=!1,$=i0,J=null,O=null,N=null,A=null,C=null,F=!1;async function ct(){try{m=JSON.parse(await lf.readFile(p,"utf8"))}catch{m={}}}function Y(){F=!0,!N&&(N=setTimeout(async()=>{if(N=null,!!F){F=!1;try{await lf.mkdir(yo.dirname(p),{recursive:!0}),await lf.writeFile(p,JSON.stringify(m))}catch(V){l.error("[plane-link] cursor flush failed:",V?.message||V)}}},jM),N.unref?.())}function T(V){return!k||k.readyState!==uf.OPEN?!1:(k.send(JSON.stringify(V)),!0)}function L(V){let B=Date.now();for(let{trackId:y,event:_}of V){let lt=g.get(_.id);g.set(_.id,{trackId:y,seq:_.seq,event:_,sentAt:B,tries:(lt?.tries??0)+1})}return T({v:Ct,ch:"events",type:"append",events:V})}function _t(V){let B=null;for(let y of g.values())y.trackId===V&&(B==null||y.seq<B)&&(B=y.seq);return B}function b(V){let B=h[V];if(B==null)return;let y=_t(V),_=y==null?B:Math.min(B,y-1);_>(m[V]??0)&&(m[V]=_,Y())}function w(){if(!S)return;let V=Date.now(),B=[...g.values()].filter(y=>y.event&&V-y.sentAt>=FM);if(B.length){B.sort((y,_)=>y.trackId===_.trackId?y.seq-_.seq:y.trackId<_.trackId?-1:1),l.log(`[plane-link] re-sending ${B.length} unacked event(s) the plane never confirmed`);for(let y=0;y<B.length;y+=Zi){let _=B.slice(y,y+Zi).map(lt=>({trackId:lt.trackId,event:lt.event}));if(!L(_))return}}}async function v(){if(!a)return;let V;try{V=await a()}catch(B){l.error("[plane-link] metrics sample failed:",B?.message||B);return}V&&T({v:Ct,ch:"ctl",type:"metrics",metrics:V})}function E(){a&&(C&&clearInterval(C),v(),C=setInterval(v,i),C.unref?.())}async function pt(){let V;try{V=await o()}catch(B){l.error("[plane-link] backfill listTracks failed:",B?.message||B);return}T({v:Ct,ch:"ctl",type:"tracks",ids:V.map(B=>B.id).filter(B=>typeof B=="string")});for(let B of V){if(!S)return;let y;try{y=await P(B)}catch{continue}let _=m[B.id]??0,lt=y.filter(st=>st.seq>_&&typeof st.id=="string");for(let st=0;st<lt.length;st+=Zi){let xt=lt.slice(st,st+Zi).map(Ft=>({trackId:B.id,event:Ft}));if(!L(xt))return}}}function q(V){!S||!V?.trackId||typeof V.id!="string"||L([{trackId:V.trackId,event:V}])}let at=new Map;async function ra(V){let{reqId:B,method:y,path:_,headers:lt={},body:st}=V,xt=Object.fromEntries(Object.entries(lt).filter(([,he])=>he!=null)),Ft=new AbortController;at.set(B,Ft);try{if(V.host){await Et(V,xt,Ft);return}let he=await fetch(c+_,{method:y,headers:xt,body:st?Buffer.from(st,"base64"):void 0,signal:Ft.signal}),on=Object.fromEntries(he.headers);for(let te of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete on[te];if(on["content-type"]||(on["content-type"]="application/octet-stream"),T({v:Ct,ch:"control",type:"http_head",reqId:B,status:he.status,headers:on}),he.body){for await(let te of he.body)if(!T({v:Ct,ch:"control",type:"http_data",reqId:B,data:Buffer.from(te).toString("base64")}))break}T({v:Ct,ch:"control",type:"http_end",reqId:B,error:null})}finally{at.delete(B)}}function Et(V,B,y){let{reqId:_,method:lt,path:st,body:xt}=V,Ft=new URL(c);return new Promise(he=>{let on=RM.request({host:Ft.hostname,port:Ft.port,method:lt,path:st,headers:{...B,host:V.host},signal:y.signal},te=>{let{connection:FD,"keep-alive":UD,"transfer-encoding":HD,upgrade:WD,...Zx}=te.headers;T({v:Ct,ch:"control",type:"http_head",reqId:_,status:te.statusCode||502,headers:Zx}),te.on("data",Qx=>{T({v:Ct,ch:"control",type:"http_data",reqId:_,data:Buffer.from(Qx).toString("base64")})||te.destroy()}),te.on("end",()=>{T({v:Ct,ch:"control",type:"http_end",reqId:_,error:null}),he()}),te.on("error",()=>{T({v:Ct,ch:"control",type:"http_end",reqId:_,error:"view upstream error"}),he()})});on.on("error",te=>{T({v:Ct,ch:"control",type:"http_end",reqId:_,error:te?.message||"view request failed"}),he()}),xt&&on.write(Buffer.from(xt,"base64")),on.end()})}function yr(){if(R)return;k=new uf(t,{headers:{authorization:`Bearer ${e}`}}),k.on("open",async()=>{let B=null;try{B=await s?.()||null}catch{B=null}T({v:Ct,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Xi,providers:MM(),...B?{agents:B}:{}})}),k.on("message",B=>{let y;try{y=JSON.parse(B)}catch{return}if(y.ch==="ctl"&&y.type==="hello_ok"){S=!0,$=i0,J=null,l.log(`[plane-link] connected to plane as ${n}`),y.minNodeVersion&&Xi&&CM(Xi,y.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${Xi}; the plane asks for \u2265${y.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),A&&clearInterval(A),A=setInterval(()=>{T({v:Ct,ch:"ctl",type:"ping"}),w()},LM),A.unref?.(),E(),pt().catch(_=>l.error("[plane-link] backfill failed:",_?.message||_));return}if(y.ch==="ctl"&&y.type==="usage_caps"){gi(y.caps??{},{source:"plane",updatedAt:y.updatedAt??Date.now()}).then(_=>l.log(`[plane-link] spend caps ${Object.keys(_.caps).length?`updated (${Object.keys(_.caps).join(", ")})`:"cleared"} from plane`)).catch(_=>l.error("[plane-link] spend-caps push rejected:",_?.message||_));return}if(y.ch==="events"&&y.type==="ack"&&Array.isArray(y.ids)){let _=new Set;for(let lt of y.ids){let st=g.get(lt);st&&(g.delete(lt),(h[st.trackId]??0)<st.seq&&(h[st.trackId]=st.seq),_.add(st.trackId))}for(let lt of _)b(lt);return}if(y.ch==="control"&&y.type==="http_open"){ra(y).catch(_=>T({v:Ct,ch:"control",type:"http_end",reqId:y.reqId,error:_?.message||"tunnel error"}));return}if(y.ch==="control"&&y.type==="http_cancel"){at.get(y.reqId)?.abort();return}if(y.ch==="attach"&&y.type==="open"){let _=new uf(u+y.path,Array.isArray(y.protocols)?y.protocols:[],y.host?{headers:{host:y.host}}:void 0);f.set(y.streamId,_),d.set(y.streamId,[]),_.on("open",()=>{let st=d.get(y.streamId)||[];d.delete(y.streamId);for(let{buf:xt,bin:Ft}of st)_.send(xt,{binary:Ft})}),_.on("message",(st,xt)=>{let Ft=xt?st:Buffer.from(String(st));T({v:Ct,ch:"attach",type:"data",streamId:y.streamId,data:Ft.toString("base64"),...xt?{bin:!0}:{}})});let lt=()=>{d.delete(y.streamId),f.delete(y.streamId)&&T({v:Ct,ch:"attach",type:"close",streamId:y.streamId})};_.on("close",lt),_.on("error",lt);return}if(y.ch==="attach"&&y.type==="data"){let _=f.get(y.streamId);if(!_)return;let lt=Buffer.from(y.data,"base64"),st=y.bin===!0,xt=d.get(y.streamId);xt?xt.push({buf:lt,bin:st}):_.send(lt,{binary:st});return}if(y.ch==="attach"&&y.type==="close"){let _=f.get(y.streamId);f.delete(y.streamId),d.delete(y.streamId),_?.close();return}});let V=()=>{S&&l.log("[plane-link] disconnected from plane"),S=!1,A&&(clearInterval(A),A=null),C&&(clearInterval(C),C=null),g.clear(),h={};for(let _ of f.values())_.close();f.clear(),d.clear();for(let _ of at.values())_.abort();if(at.clear(),R||O)return;J==null&&(J=Date.now());let B=Date.now()-J<PM,y=B?DM+Math.random()*BM:$;O=setTimeout(()=>{O=null,yr()},y),O.unref?.(),B||($=Math.min($*2,NM))};k.on("close",V),k.on("error",V)}return qt.on("event",q),ct().then(yr),{stop:()=>{R=!0,qt.off("event",q),O&&clearTimeout(O),N&&clearTimeout(N),A&&clearInterval(A),C&&clearInterval(C),T({v:Ct,ch:"ctl",type:"bye",reason:"shutdown"}),k?.close()},status:()=>({connected:S,nodeId:n,cursor:{...m},inflight:g.size})}}import HM from"node:fs";import l0 from"node:fs/promises";import qs from"node:os";import WM from"node:path";var qM=1024*1024,tc=t=>Math.round(t/qM);async function KM(t){try{let e=await l0.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:tc(r),diskTotalMb:tc(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function GM(){try{return(await ae()).filter(e=>e.isBostrat).length}catch{return null}}async function VM(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await l0.readdir(WM.join(Ws,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function u0({stateDir:t=Ws,now:e=Date.now}={}){let n=qs.totalmem(),[r,o,s,a]=await Promise.all([Ha().catch(()=>null),KM(HM.existsSync(t)?t:qs.tmpdir()),GM(),VM()]),i=qs.cpus()?.length||null,c=qs.loadavg()?.[0]??null;return{ts:e(),loadavg1:c!=null?Math.round(c*100)/100:null,cpus:i,cpuPct:c!=null&&i?Math.min(100,Math.round(c/i*100)):null,memUsedMb:r!=null?tc(n-r):null,memTotalMb:tc(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(qs.uptime())}}function df(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 d0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),f0=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},ec=t=>Math.round(t*1e4)/1e4;async function p0(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=d0(),f=0,d=[];for(let p of t||[]){let m=await P(p),h=ke(m,{boxLabels:df(p)});if(h.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.asRecordedUsd||0,c+=h.unpricedTurns||0;for(let g of h.unpricedModels||[])l.add(g);f0(u,h.totalTokens);for(let[g,k]of Object.entries(h.byModel))n[g]||={usd:0,events:0,tokens:d0()},n[g].usd=ec(n[g].usd+k.usd),n[g].events+=k.events,f0(n[g].tokens,k.tokens);for(let[g,k]of Object.entries(h.byProvider))r[g]||={usd:0,events:0},r[g].usd=ec(r[g].usd+k.usd),r[g].events+=k.events;e.push({id:p.id,name:p.name,usd:h.totalUsd,rollup:h})}return e.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:ec(o),asRecordedUsd:ec(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:e}}var Nn=t=>`$${t.toFixed(4)}`,bo=(t,e)=>String(t).padEnd(e);function m0(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 ${Nn(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) priced at current table rates (differs from as-recorded)`),e.asRecordedUsd!=null&&Math.abs(e.asRecordedUsd-e.totalUsd)>=.005&&r.push(`as-recorded total ${Nn(e.asRecordedUsd)} \xB7 current-rates total ${Nn(e.totalUsd)} (${e.totalUsd>=e.asRecordedUsd?"+":""}${Nn(e.totalUsd-e.asRecordedUsd)})`),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(` ${bo(a,26)} ${bo(Nn(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(` ${bo(a.name||a.id,30)} ${Nn(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${bo(c.label,40)} ${bo(Nn(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((f,d)=>Number(f[0])-Number(d[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${bo(Nn(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
420
+ `)}import ZM from"node:fs/promises";import QM from"node:path";var nc=t=>t.payload?.ts??t.ts,zM=12e4,Pn=t=>Math.round(t*1e4)/1e4,JM=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function YM(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>nc(S)>=e,a=Vn([qo(n?.name),qo(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 R=Vn(S.payload.task);R&&i.set(S.boxId,R)}let c=new Map,l=S=>{let R=S||"unattributed";return c.has(R)||c.set(R,{segments:[u(null)],lastTs:null,provider:null}),c.get(R)},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}),f=[...r].sort((S,R)=>nc(S)-nc(R)),d=0,p=0,m=0,h={turns:0};for(let S of f){let R=l(S.boxId),$=R.segments[R.segments.length-1],J=nc(S);switch(R.lastTs!=null&&J>R.lastTs&&s(S)&&($.activeMs+=Math.min(J-R.lastTs,zM)),R.lastTs=J,S.provider&&(R.provider=S.provider),S.type){case"turn.user":{let O=S.payload?.text;if(S.source==="hook"||!String(O||"").trim())break;let N=u(Vn(O));R.segments.push(N),s(S)&&(N.turns+=1);break}case"peer.message":{let O=S.payload?.text;if(!String(O||"").trim())break;let N=u(Vn(O));R.segments.push(N),s(S)&&(N.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let O=S.payload?.tool;O&&($.tools[O]=($.tools[O]||0)+1),s(S)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let{usd:O,tokens:N,model:A,unpriced:C}=je(S);C&&(h.turns+=1),$.usd+=O,N&&($.tokens.input+=N.input,$.tokens.output+=N.output,$.tokens.cacheRead+=N.cacheRead,$.tokens.cacheWrite+=N.cacheWrite),$.models[A]=($.models[A]||0)+O,S.provider&&($.provider=S.provider);break}case"handoff.done":s(S)&&(d+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let g=new Map,k=!1;for(let[S,R]of c)for(let $ of R.segments){if(!($.usd>0||$.activeMs>0||$.turns>0||$.toolCalls>0))continue;let O=$.textCat?.category??i.get(S)?.category??am($.tools)?.category??a?.category??"uncategorized";g.has(O)||g.set(O,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let N=g.get(O);N.usd+=$.usd,N.activeMs+=$.activeMs,N.turns+=$.turns,N.toolCalls+=$.toolCalls;for(let[C,F]of Object.entries($.models))N.models[C]=(N.models[C]||0)+F;let A=$.provider||R.provider;A&&(N.providers[A]=(N.providers[A]||0)+$.usd),A==="cursor"&&$.usd===0&&(k=!0)}return{perCategory:g,handoffs:d,merged:p,failed:m,sawCursorActivity:k,unpricedTurns:h.turns}}function w0(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 h of t||[]){let{track:g,events:k}=h;if(!k?.length){a+=1;continue}let{perCategory:S,handoffs:R,merged:$,failed:J,sawCursorActivity:O,unpricedTurns:N}=YM(h,{since:r});O&&(i=!0),c+=N;let A={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:R,subtasksMerged:$,subtasksFailed:J};for(let[C,F]of S){A.usd+=F.usd,A.activeMs+=F.activeMs,A.turns+=F.turns,A.categories[C]={usd:Pn(F.usd),activeMs:F.activeMs};for(let[T,L]of Object.entries(F.models))A.models[T]=Pn((A.models[T]||0)+L);for(let[T,L]of Object.entries(F.providers))A.providers[T]=Pn((A.providers[T]||0)+L);o.has(C)||o.set(C,JM());let ct=o.get(C);ct.usd+=F.usd,ct.activeMs+=F.activeMs,ct.turns+=F.turns,ct.toolCalls+=F.toolCalls;for(let[T,L]of Object.entries(F.models))ct.models[T]=(ct.models[T]||0)+L;for(let[T,L]of Object.entries(F.providers))ct.providers[T]=(ct.providers[T]||0)+L;let Y=ct.byTrack.get(g?.id)||{usd:0,activeMs:0};Y.usd+=F.usd,Y.activeMs+=F.activeMs,ct.byTrack.set(g?.id,Y)}(A.usd>0||A.activeMs>0||A.turns>0)&&(A.usd=Pn(A.usd),A.dominantCategory=XM(A.categories),s.push(A))}let l=[...o.values()].reduce((h,g)=>h+g.usd,0),u=[...o.values()].reduce((h,g)=>h+g.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((t||[]).map(h=>[h.track?.id,h.track?.name||h.track?.id])),m=[...o.entries()].map(([h,g])=>({category:h,sharePct:d>0?Math.round((f==="usd"?g.usd:g.activeMs)/d*100):0,shareBasis:f,usd:Pn(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:h0(g.models),providers:h0(g.providers),topWorkstreams:[...g.byTrack.entries()].sort((k,S)=>S[1].usd-k[1].usd||S[1].activeMs-k[1].activeMs).slice(0,2).map(([k,S])=>({trackId:k,name:p.get(k),usd:Pn(S.usd),activeMs:S.activeMs}))})).sort((h,g)=>g.usd-h.usd||g.activeMs-h.activeMs);return s.sort((h,g)=>g.usd-h.usd||g.activeMs-h.activeMs),{since:r,capturedAt:n,totalUsd:Pn(l),activeMs:u,turns:[...o.values()].reduce((h,g)=>h+g.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var XM=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},h0=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Pn(n)]));var ko={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function tN(t){let e=String(t||"7d");return e in ko?{key:e,ms:ko[e]}:{key:"7d",ms:ko["7d"]}}function eN(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=ki(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 nN(t){if(!t?.cwd)return null;try{let n=(await ZM.readFile(QM.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 g0(t,{window:e="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:tN(e),f=[];for(let d of t||[]){let p=await P(d),m=eN(d,p);i&&c<o&&(m=m.filter(h=>(h.ts??0)<=c)),f.push({track:d,events:m,brief:await nN(d)})}return{window:l,...w0(f,{windowMs:u,now:c})}}var Dn=36e5,x0=24*Dn,rN=400,oN=12,xo=t=>Math.round(t*1e4)/1e4;function sN({window:t,from:e,to:n,now:r=Date.now()}={}){let o=Number(e),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(t||"7d")in ko?String(t||"7d"):"7d",i=ko[a];return{key:a,since:i==null?null:r-i,until:r}}var aN=(t,e)=>t!=null&&e-t<=48*Dn?Dn:x0;function y0(t,e){if(e===Dn)return Math.floor(t/Dn)*Dn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function iN(t,e){if(e===Dn)return t+Dn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var b0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),k0=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},cN=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,xo(n)]));async function S0(t,e={}){let n=e.now??Date.now(),r=sN({...e,now:n}),o=r.since==null?x0:aN(r.since,r.until),s=A=>(r.since==null||A>=r.since)&&A<r.until,a=r.since!=null?r.until-r.since:null,i=A=>a!=null&&A>=r.since-a&&A<r.since,c={},l={},u=b0(),f=0,d=0,p=0,m=0,h=0,g=new Set,k=new Map,S=[],R=[],$=[],J=null;for(let A of t||[]){let F=(await P(A)).filter(T=>T.type==="cost.recorded");if(!F.length){A.archivedAt||$.push(A.name||A.id);continue}let ct=[];for(let T of F){let L=T.ts??0;s(L)?ct.push(T):i(L)&&(h+=je(T).usd)}if(!ct.length)continue;let Y=ke(ct,{boxLabels:df(A)});f+=Y.totalUsd,d+=Y.turns,p+=Y.repricedTurns||0,m+=Y.unpricedTurns||0;for(let T of Y.unpricedModels||[])g.add(T);k0(u,Y.totalTokens);for(let[T,L]of Object.entries(Y.byModel))c[T]||={usd:0,events:0,tokens:b0()},c[T].usd=xo(c[T].usd+L.usd),c[T].events+=L.events,k0(c[T].tokens,L.tokens);for(let[T,L]of Object.entries(Y.byProvider))l[T]||={usd:0,events:0},l[T].usd=xo(l[T].usd+L.usd),l[T].events+=L.events;for(let T of ct){let{usd:L}=je(T);if(!L)continue;let _t=y0(T.ts??0,o),b=k.get(_t)??{t:_t,total:0,byProvider:{}};b.total+=L;let w=T.provider||"unknown";b.byProvider[w]=(b.byProvider[w]||0)+L,k.set(_t,b),(J==null||_t<J)&&(J=_t)}S.push({id:A.id,name:A.name||A.id,archived:!!A.archivedAt,usd:Y.totalUsd,turns:Y.turns,providers:Object.keys(Y.byProvider)});for(let[T,L]of Object.entries(Y.byBox))R.push({trackId:A.id,trackName:A.name||A.id,boxId:T,...L})}let O=r.since!=null?y0(r.since,o):J,N=[];for(let A=O,C=0;A!=null&&A<r.until&&C<rN;A=iN(A,o),C++){let F=k.get(A);N.push(F?{t:A,total:xo(F.total),byProvider:cN(F.byProvider)}:{t:A,total:0,byProvider:{}})}return S.sort((A,C)=>C.usd-A.usd),R.sort((A,C)=>C.usd-A.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:N,fleet:{totalUsd:xo(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...g],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:$},prior:a!=null?{totalUsd:xo(h),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:R.slice(0,oN)}}import So from"node:fs/promises";import lN from"node:fs";import Pt from"node:path";import uN from"node:os";import{execFile as dN}from"node:child_process";import{promisify as fN}from"node:util";var pN=fN(dN),v0=process.env.BOSTRAT_STATE_DIR||Pt.join(process.env.HOME||uN.tmpdir(),".bostrat"),rc=!1,ff=null;async function pf(t,e){let n=await So.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await So.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await So.mkdir(Pt.dirname(e),{recursive:!0}),await So.copyFile(t,e),!0)}var mN=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function oc(t,e,{skip:n=null}={}){let r=0,o=await So.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(mN.has(s.name)||n?.has(s.name))continue;let a=Pt.join(t,s.name);s.isDirectory()?r+=await oc(a,Pt.join(e,s.name)):s.isFile()&&(r+=await pf(a,Pt.join(e,s.name))?1:0)}return r}async function T0({stateDir:t=v0,dest:e=process.env.BOSTRAT_SYNC_DIR||`${v0}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await So.mkdir(e,{recursive:!0});let o=0;o+=await pf(Pt.join(t,"tracks.json"),Pt.join(e,"tracks.json"))?1:0,o+=await pf(Pt.join(t,"events.jsonl"),Pt.join(e,"events.jsonl"))?1:0,o+=await oc(Pt.join(t,"events"),Pt.join(e,"events")),o+=await oc(Pt.join(t,"memory"),Pt.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await oc(Pt.join(u.cwd,".bostrat"),Pt.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>pN("git",["-C",e,...u]);lN.existsSync(Pt.join(e,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Rc(e).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||rc))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,rc&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),rc=!1,ff=null}catch(u){rc=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
421
+ `).slice(-3).join(" | ").slice(0,300);f!==ff&&(ff=f,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${f}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as hN}from"node:child_process";import{promisify as wN}from"node:util";var gN=wN(hN),yN=12e4,bN=6e4;function kN(t){return`refs/bostrat/wip/${t}`}function E0(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function xN(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:yN}var sc=(t,e,n=15e3)=>gN("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),A0=new Map,_0=new Set;async function SN(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await sc(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await sc(r,["remote","get-url","origin"])}catch{return _0.has(r)||(_0.add(r),e.log?.(`[wip-sync] ${n}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let o=await ys(r,"bostrat: wip snapshot"),{stdout:s}=await sc(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(A0.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=kN(n);return await sc(r,["push","--force","origin",`${o}:${i}`],bN),A0.set(r,a),await I({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
422
+ `)[0]}`),{pushed:!1,reason:"push-failed"}}}function I0({listTracks:t,sweepMs:e=xN(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await t()??[];for(let i of a)i?.archivedAt||i?.kind==="setup"||await SN(i,{log:n})}catch(a){n.log?.(`[wip-sync] sweep failed: ${a?.message||a}`)}finally{r=!1}}},s=setInterval(o,e);return s.unref?.(),o(),{stop:()=>clearInterval(s),cycle:o}}import{execFile as vN}from"node:child_process";import{promisify as TN}from"node:util";import{randomUUID as AN}from"node:crypto";import _N from"node:fs";import EN from"node:os";import IN from"node:path";var jn=TN(vN),ac=t=>new Promise(e=>setTimeout(e,t)),Ln=Gc,mf=process.env.BOSTRAT_USAGE_PROBE_CWD||IN.join(EN.homedir(),".bostrat","usage-probe"),RN=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,$N=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,ON=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,R0=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Bn=null,Ks=null,Gs=null;async function CN(){try{return await jn(M,D("has-session","-t",Ln)),!0}catch{return!1}}function MN(){Bn&&clearTimeout(Bn),Bn=setTimeout(()=>NN().catch(()=>{}),RN),Bn.unref&&Bn.unref()}async function NN(){Bn&&(clearTimeout(Bn),Bn=null);try{await jn(M,D("kill-session","-t",Ln))}catch{}}async function PN(){let t=Date.now()+ON;for(;;){let e=await hf().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await ac(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await ac(500)}}async function DN(){if(await CN())return;if(!await dn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");_N.mkdirSync(mf,{recursive:!0}),await sn(mf);let t=AN();await jn(M,D("new-session","-d","-s",Ln,"-c",mf,"-x","200","-y","60",...Kc()));let e=`${qc} --session-id ${t}`;await jn(M,D("send-keys","-t",Ln,e,"Enter")),await PN()}async function hf(){let{stdout:t}=await jn(M,D("capture-pane","-t",Ln,"-p"));return t}async function BN(){await DN(),await jn(M,D("send-keys","-t",Ln,"-l","/usage")),await jn(M,D("send-keys","-t",Ln,"Enter"));let t=Date.now()+$N,e="";for(;await ac(400),e=await hf(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await ac(900),e=await hf(),await jn(M,D("send-keys","-t",Ln,"Escape")).catch(()=>{}),MN(),e}function jN(t){let e=String(t||"").split(`
423
+ `).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 LN(t){let e=String(t||"").split(`
424
+ `).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 $0({fresh:t=!1}={}){return!t&&Ks&&Date.now()-Ks.at<R0?Ks.data:Gs||(Gs=(async()=>{try{let e=await BN(),n=jN(e),r={...n,contributors:LN(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ks={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 Ks={at:Date.now()-(R0-3e3),data:n},n}finally{Gs=null}})(),Gs)}var FN=262144;function UN(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 O0(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:UN(t.resets_in_seconds)}:null}function HN(t){let e=String(t||"").split(`
425
+ `),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:O0(i.primary),week:O0(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 wf(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await zn(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 Wt(n.file,FN),o=HN(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 C0(t,{fresh:e=!1}={}){let n=new Set;for(let o of t||[])for(let s of o.boxes||[])s.agent&&n.add(s.agent);let r=[];if((n.has("claude")||n.size===0)&&r.push(await ic({agent:"claude"},{fresh:e})),n.has("codex")){let o=[],s=[...t||[]].sort((i,c)=>(i.archivedAt?1:0)-(c.archivedAt?1:0)||(c.createdAt||0)-(i.createdAt||0));for(let i of s)for(let c of i.boxes||[])c.agent==="codex"&&o.push({...c,cwd:c.cwd||i.cwd});let a=null;for(let i of o.slice(0,8)){let c=await wf(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await wf(null))}return n.has("cursor")&&r.push(await ic({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function ic(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?wf(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await $0({fresh:e})}}function WN(t=process.env){return!!(Dt(t)&&t.BOSTRAT_PLANE_TOKEN)}var qN=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),cc=(t,e)=>Object.assign(new Error(t),{status:e});async function M0(t,e){if(!WN(e))throw cc("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Dt(e)}${t}`,{headers:qN(e)})}catch(o){throw cc(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw cc(r?.error||`plane replied ${n.status}`,n.status);return r}async function N0(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await M0(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function P0(t,{env:e=process.env}={}){if(!t)throw cc("message id required",400);return(await M0(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function gf(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 KN(t){let[e,n]=t.split("/"),r=gf(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 D0(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(KN).filter(Boolean)}function GN(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function B0(t){let e=D0(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:GN,allowed(r){if(n)return!0;let o=gf((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function j0(t,e){let n=D0(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=gf(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import L0 from"node:crypto";import lc from"node:fs";import VN from"node:path";function F0(t=process.env,{stateDir:e=Ws,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=VN.join(e,"api-token");try{let a=lc.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=L0.randomBytes(32).toString("base64url");try{lc.mkdirSync(e,{recursive:!0}),lc.writeFileSync(o,s+`
426
+ `,{mode:384}),lc.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Vs(t){let e=t?.socket?.remoteAddress||"";if(!e)return!1;let n=e.replace(/^::ffff:/,"");return n==="::1"||n==="127.0.0.1"||/^127\./.test(n)}function U0(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:L0.timingSafeEqual(n,r)}function H0(t,e=null){let n=t?.headers?.["x-bostrat-token"];if(typeof n=="string"&&n.trim())return n.trim();let r=t?.headers?.authorization||"",o=/^Bearer\s+(.+)$/i.exec(r)?.[1];if(o)return o.trim();try{return(e??new URL(t.url,"http://node")).searchParams.get("token")||""}catch{return""}}var zN=[/^\/api\/github\/token\b/],JN=t=>zN.some(e=>e.test(t));function W0({token:t,enforce:e=!0,log:n=console}={}){return function(o,s,a){let i=o.path||o.url||"";if(!i.startsWith("/api"))return a();let c=H0(o);return U0(c,t)?a():JN(i)?(n.log?.(`[perimeter] refused ${o.method} ${i} \u2014 sensitive route requires the node API token`),s.status(401).json({error:"this route requires the node API token"})):!e||Vs(o)?a():(n.log?.(`[perimeter] refused ${o.method} ${i} from ${o.socket?.remoteAddress||"?"} \u2014 no node API token`),s.status(401).json({error:"unauthorized"}))}}function q0(t,e,{enforce:n=!0}={}){return!n||U0(H0(t),e)?!0:Vs(t)}import YN from"node:http";import XN from"node:net";function K0(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 uc(t){let e=K0(t.headers.host);return e?{slug:e,view:Jl(e)}:null}var ZN=t=>`<!doctype html><meta charset="utf-8">
413
427
  <meta http-equiv="refresh" content="3">
414
428
  <title>${t.name} \u2014 starting</title>
415
429
  <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
416
430
  <div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
417
- <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function $0(t,e,n){let r=sc(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(IN(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=_N.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 O0(t,e,n){let r=sc(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
431
+ <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function G0(t,e,n){let r=uc(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(ZN(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=YN.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 V0(t,e,n){let r=uc(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
418
432
  \r
419
433
  bostrat: no live view "${s}"\r
420
- `),e.destroy(),!0;let a=EN.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
434
+ `),e.destroy(),!0;let a=XN.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
421
435
  `)+`\r
422
436
  \r
423
- `),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 M0(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}.${RN(n.host)}/`}function RN(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 R0(e)&&Wl(n[0])?n.slice(1).join("."):e}var C0=10,N0=200,$N=120,ON=24,MN=/^\d{4}-\d{2}-\d{2}$/;function df(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var CN=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function P0(t,e=Date.now()){if(!t)return e-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(t).trim());if(n)return e-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(t);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${t}" \u2014 use 24h, 7d, or an ISO date`)}function NN(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=es(t);if(e.length)throw new Error(`changelog entry appears to contain a secret VALUE (${e.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of t.split(`
424
- `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,$N)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${ON}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>N0)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${N0}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>C0)throw new Error(`${r.length} bullets \u2014 over the ${C0}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function D0(t,{repos:e=df()}={}){let n=new Set(e);return(await Ee()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=t&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,mergedAt:r.resolvedAt??null}))}async function B0(t,{md:e,day:n=null}){let r=NN(e),o=n==null||n===""?CN():String(n);if(!MN.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await $({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}import{execFile as PN}from"node:child_process";import{promisify as DN}from"node:util";var pf=DN(PN),j0=3e4,BN=30*6e4,jN=j0,LN=10*6e4,FN=10080*6e4,UN=60*6e4,HN=8,WN=1440*6e4,qN=3,L0="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ff=60*6e4,KN=15*6e4,ac=new Map,ic=new Map,GN=/could not resolve|not found|no pull requests?/i,VN=t=>GN.test(String(t?.message||t||""));function zN(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<ff)}var F0={fast:jN,slow:LN,decay:UN};function U0(t,e){return t.armedAt&&e-t.armedAt<BN||zN(t,e)?"fast":e-(t.firstSeen||0)>FN?"decay":"slow"}function JN(t,e){return F0[U0(t,e)]}function Ks(t,e,n,r){let o=t.lanes?.get(K(e.repo,e.number))||{};return Ri({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function XN(t,e){Je("watch");try{let{stdout:n}=await pf("gh",["pr","view",String(e),"--repo",t,"--json",L0],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await me({slug:t})}});return JSON.parse(n||"null")}catch(n){throw An(n)&&_n(n),n}}async function YN(t){Je("watch");try{let{stdout:e}=await pf("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",L0],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await me({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw An(e)&&_n(e),e}}async function ZN(){let t=new Map;for(let e of await st().catch(()=>[]))t.set(e.id,e);return t}async function QN(t,e){await $({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 tP(t,e){let n=await fr(t);return n?ro(n,e):null}async function eP(t,e,n){let r=await fr(t);if(!r)throw new Error(`no local checkout for ${t}`);return oo(r,e,{method:n})}async function nP(t,e){let n=await fr(t);if(!n)throw new Error(`no local checkout for ${t}`);return Ss(n,e)}async function rP(t,e){await $({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 vo(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 oP=10*6e4;async function sP(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>WN){await vo(t,null,e,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(t.repo,t.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await vo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await vo(t,o,e,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(t.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<oP)return;await vo(t,o,e,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(t.repo,t.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(t.repo,t.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await vo(t,o,e,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(t.repo,t.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await vo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await H0(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Hs(t,e,n){await $({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 aP(t,e){try{return await pf("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ws(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 qs=(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 iP(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 ei(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await ri(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait),l=!!t.baseFoldAt&&o-t.baseFoldAt<KN;await zw(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await Vw(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ws(a,"pushed",t.number,f=>f.payload?.sha===s)&&await Hs(a,"pushed",{...qs(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>ff)await ni(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 ni(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Ks(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Ws(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await Hs(a,"ci-fixed",{...qs(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await ni(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>ff)await oi(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await oi(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Ks(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Ws(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Hs(a,"reconciled",{...qs(t,e),base:i.baseRefName||null}).catch(()=>{}),await oi(t.repo,t.number).catch(()=>{})}}}}async function cP(t,e,n,r){let o=Ks(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Ws(s,"closed",t.number)&&await Hs(s,"closed",{...qs(t,e),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(t.repo,t.number,"closed").catch(()=>{}),await r.mark(t.repo,t.number,"closed").catch(()=>{})}async function lP(t,e,n){Ks(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Ws(r,"lost",t.number)&&await Hs(r,"lost",qs(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function H0(t,e,n,r,o={}){let s=Ks(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await B(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(t.number))||await r.emitMerged(a,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(t.repo,t.number).catch(()=>{}),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function uP(t={}){let{now:e=Date.now(),auth:n=ud,view:r=XN,list:o=YN,links:s=Xw,trackMap:a=ZN,emitMerged:i=QN,mark:c=vn,archiveIfArmed:l=In,notifyIfArmed:u=Rn,notifyIfResolved:f=Ld,batchThreshold:d=qN,detail:p=tP,merge:m=eP,updateBranch:h=nP,lookup:y=Bt,disarm:k=ds,emitDisarmed:v=rP,localHasCommit:I=aP}=t;if(!(await n().catch(()=>null))?.authenticated||ki()>0)return 0;let O=(await s().catch(()=>[])).filter(T=>e-(ac.get(K(T.repo,T.number))||0)>=JN(T,e));if(!O.length)return 0;let M=Date.now(),S=new Map,N=new Map;for(let T of O){let g=U0(T,e);S.set(K(T.repo,T.number),{lane:g,intervalMs:F0[g]}),ac.set(K(T.repo,T.number),e),N.has(T.repo)||N.set(T.repo,[]),N.get(T.repo).push(T)}let D=await a().catch(()=>new Map),it={now:e,lanes:S,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:h,lookup:y,disarm:k,emitDisarmed:v,localHasCommit:I};for(let[T,g]of N){let w,A=new Set;if(g.length>d)w=await o(T).catch(()=>null);else{w=new Map;for(let E of g)try{let J=await r(T,E.number);J?.number!=null&&w.set(Number(J.number),J)}catch(J){VN(J)&&A.add(K(E.repo,E.number))}}if(w)for(let E of g){let J=K(E.repo,E.number),W=w.get(Number(E.number));if(!W){if(A.has(J)){let at=(ic.get(J)||0)+1;ic.set(J,at),at>=HN&&(await lP(E,D,it),ic.delete(J),ac.delete(J))}continue}ic.delete(J),W.state==="MERGED"||W.state==="CLOSED"?(W.state==="MERGED"?await H0(E,W,D,it):await cP(E,W,D,it),ac.delete(J)):(await iP(E,W,D,it).catch(()=>{}),E.autoMerge&&await sP(E,D,it).catch(()=>{}))}}let ct={};for(let{lane:T}of S.values())ct[T]=(ct[T]||0)+1;return console.log(`[pr-watch] probed ${O.length} link(s) (${Object.entries(ct).map(([T,g])=>`${T} ${g}`).join(", ")}) in ${Date.now()-M}ms`),O.length}function W0(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await uP()}catch{}finally{t=!1}}},j0)}import nn from"node:fs/promises";import So from"node:path";import X0 from"node:os";import q0 from"node:fs/promises";import cc from"node:path";import{execFile as dP}from"node:child_process";import{promisify as fP}from"node:util";import{fileURLToPath as pP}from"node:url";var mP=fP(dP),G0=cc.dirname(pP(import.meta.url)),hP=cc.resolve(G0,"..","..","client","dist"),wP="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",gP=new Map([...wP].map((t,e)=>[t,e]));function yP(t){let e=[],n=0,r=0;for(let o of t){let s=gP.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 bP(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=yP(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 kP(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 K0=new Map;async function xP(t){let e;try{e=await q0.stat(t)}catch{return null}let n=K0.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await q0.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:bP(r.mappings||"")};return K0.set(t,o),o}catch{return null}}var mf=null;function vP(){return mf||(mf=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):mP("git",["rev-parse","--short","HEAD"],{cwd:cc.resolve(G0,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),mf}var SP=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function TP(t,{distDir:e=hP}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(SP)){let[a,i,c,l]=s,u=await xP(cc.join(e,"assets",`${i}.map`)),f=u?kP(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[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 V0(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await vP();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await TP(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var AP=()=>process.env.BOSTRAT_STATE_DIR||So.join(process.env.HOME||X0.tmpdir(),".bostrat"),Ln=()=>So.join(AP(),"telemetry"),Y0=()=>So.join(Ln(),".relay-offsets.json"),_P=50,EP=14,IP=3e4,z0=500,Yt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function RP(t,e){if(!t||typeof t!="object")return null;let n=Yt(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:Yt(t.anonId,64),sessionId:Yt(t.sessionId,64),userId:Yt(t.userId,64),release:Yt(t.release,64),appVersion:Yt(t.appVersion,32),route:Yt(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:Yt(t.error.kind,256),message:Yt(t.error.message,8192),stack:Yt(t.error.stack,32768),fingerprint:Yt(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:Yt(s?.n,64),r:Yt(s?.r,128)}))),o}var Z0=t=>new Date(t).toISOString().slice(0,10),J0=0;async function Q0(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,_P):[]).map(s=>RP(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await V0(s);await nn.mkdir(Ln(),{recursive:!0});let o=So.join(Ln(),`${Z0(e)}.ndjson`);return await nn.appendFile(o,r.map(s=>JSON.stringify(s)+`
425
- `).join(""),"utf8"),e-J0>6*3600*1e3&&(J0=e,$P(e).catch(()=>{})),r.length}async function $P(t=Date.now()){let e=Z0(t-EP*24*3600*1e3),n=[];try{n=await nn.readdir(Ln())}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 nn.rm(So.join(Ln(),s),{force:!0});let o=await ex();for(let s of r)delete o[s];await nx(o)}var lc=new Map;function tx(t,e=Date.now()){let n=t||"unknown",r=lc.get(n);return r||(lc.size>512&&lc.clear(),r={tokens:40,at:e},lc.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 ex(){try{return JSON.parse(await nn.readFile(Y0(),"utf8"))||{}}catch{return{}}}async function nx(t){await nn.mkdir(Ln(),{recursive:!0}),await nn.writeFile(Y0(),JSON.stringify(t),"utf8")}function OP(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||X0.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 MP({fetchImpl:t=fetch}={}){let e=await hw();if(!e)return{sent:0,disabled:!0};let n;try{n=(await nn.readdir(Ln())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await ex(),o=0;for(let s of n){let a=So.join(Ln(),s),i=r[s]||0,c;try{let d=await nn.open(a,"r");try{let p=(await d.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
426
- `).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=z0){let p=f.slice(d,d+z0).map(h=>OP(h)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await nx(r)}return{sent:o,disabled:!1}}var hf=null,wf=!1;function rx(){hf||(hf=setInterval(async()=>{if(!wf){wf=!0;try{await MP()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{wf=!1}}},IP),hf.unref?.())}import dc from"node:fs/promises";import cx from"node:path";import CP from"node:os";var lx=()=>process.env.BOSTRAT_STATE_DIR||cx.join(process.env.HOME||CP.tmpdir(),".bostrat"),ux=()=>cx.join(lx(),"dictation-glossary.json"),NP=100,PP=500,DP=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],fc=null,gf=null,yf=Promise.resolve();function ox(){let t={},e=Date.now();for(let n of DP)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function dx(){return fc||(gf??=dc.readFile(ux(),"utf8").then(t=>JSON.parse(t)?.terms??ox()).catch(()=>ox()).then(t=>fc=t).finally(()=>gf=null),gf)}async function sx(){let t=JSON.stringify({terms:fc},null,2),e=ux(),n=`${e}.${process.pid}.tmp`;await dc.mkdir(lx(),{recursive:!0}),await dc.writeFile(n,t),await dc.rename(n,e)}function BP(){return yf=yf.then(sx,sx),yf.catch(()=>{})}async function bf(){return Object.values(await dx()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var jP=t=>Math.max(1,Math.ceil(t.length/4));async function LP(){let t=[],e=0;for(let{term:n}of await bf()){let r=jP(n);if(!(e+r>PP)&&(t.push(n),e+=r,t.length>=NP))break}return t}async function FP(t,e){let n=await dx(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var ax=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),uc=t=>t.toLowerCase();function ix(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function fx(t,e){let n=ax(t),r=ax(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 f=s-1;f>=0;f--)a[u][f]=uc(n[u])===uc(r[f])?a[u+1][f+1]+1:Math.max(a[u+1][f],a[u][f+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)uc(n[c])===uc(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(ix(r[l])&&i.add(r[l]),l++);for(;l<s;l++)ix(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await FP(u,"learned");return await BP(),[...i]}var Gs=null;function UP(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 px(){if(!Gs)return null;let t=await rs();if(!t||t.apiKey.slice(-6)!==Gs.keyTail)return null;let{keyTail:e,...n}=Gs;return n}async function mx(t,e){let n=await rs();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 LP())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=UP(o.status,i);c.persistent&&(Gs={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 Gs=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var kf={"gpt-realtime-2.1":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime":{textIn:4,textInCached:.4,textOut:16,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2.1-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20},"gpt-realtime-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20}};function HP(t){if(!t)return null;let e=String(t).trim();if(kf[e])return e;let n=null;for(let r of Object.keys(kf))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function WP(t,e={}){let n=kf[HP(t)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(e[o])||0)*n[o];return r/1e6}function qP(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.error?.code||n?.error?.type||"";return t===429&&/insufficient_quota/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"The OpenAI account is out of credit \u2014 voice chat is down until you top up at platform.openai.com or drop a new key into ~/.bostrat/integrations.json."}:t===401||t===403?{status:502,code:"key_invalid",persistent:!0,message:"OpenAI rejected the API key (revoked, or missing Realtime access) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"mint_failed",persistent:!1,message:`OpenAI realtime session mint failed (${t}): ${e.slice(0,300)}`}}var Vs=null;async function KP(){if(!Vs)return null;let t=await Xa();if(!t||t.apiKey.slice(-6)!==Vs.keyTail)return null;let{keyTail:e,...n}=Vs;return n}async function GP(t){let e=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${t.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:t.model,audio:{output:{voice:t.voice}}}})});if(!e.ok){let o=await e.text().catch(()=>""),s=qP(e.status,o);s.persistent&&(Vs={code:s.code,message:s.message,at:Date.now(),keyTail:t.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}Vs=null;let n=await e.json(),r=n?.value??n?.client_secret?.value;if(!r){let o=new Error("OpenAI returned no client secret");throw o.status=502,o.code="mint_failed",o}return{clientSecret:{value:r,expiresAt:n?.expires_at??n?.client_secret?.expires_at??null}}}var VP={openai:GP};async function hx(){let t=await Xa();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await KP()}:{configured:!1}}async function wx(){let t=await Xa();if(!t){let n=new Error("Voice chat is not configured \u2014 add openai.apiKey to ~/.bostrat/integrations.json");throw n.status=503,n.code="not_configured",n}let e=await VP[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function gx(t,{model:e,usage:n={},seconds:r}={}){let o=c=>Math.max(0,Math.round(Number(c)||0)),s={textIn:o(n.textIn),textInCached:o(n.textInCached),textOut:o(n.textOut),audioIn:o(n.audioIn),audioInCached:o(n.audioInCached),audioOut:o(n.audioOut)},a=WP(e,s),i=await $(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var Sx=ft.dirname(JP(import.meta.url)),yx=Number(process.env.PORT)||3362,pc=Wk(process.env),Tx=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,To=x0(process.env.BOSTRAT_ALLOW_IPS),Af=v0(process.env.BOSTRAT_VIEW_FRONT_CIDRS,sc),Ax=T0(),_x=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=fe();x.disable("x-powered-by");x.use((t,e,n)=>{if(Us(t)||To.allowed(To.clientIp(t))||Af.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use($0);x.use(E0({token:Ax,enforce:_x}));x.use("/api/mocks",fe.json({limit:"1mb"}));x.use("/api/mocks",fe.raw({type:"image/png",limit:"24mb"}));x.use("/api/mocks",fe.raw({type:["video/mp4","video/webm"],limit:"105mb"}));x.use("/api/runbooks",fe.json({limit:"1mb"}));x.use("/api/telemetry",fe.json({limit:"256kb"}));x.use("/api/hook",fe.json({limit:"2mb"}));x.use("/api/dictation",fe.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",fe.raw({type:()=>!0,limit:"25mb"}));x.use(fe.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:zt,hostTmuxAlive:await un(),autoTriggerPct:Tx})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await Ce()})});x.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 qn(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})}});x.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});if(!await tt(n))return e.status(404).json({error:"session not found"});try{await we(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});x.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});let r=await tt(n);if(!r)return e.status(404).json({error:"session not found"});let o=await jo(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});x.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});try{let r=await oh(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)})}});x.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=Pa(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})});var bx=10;x.get("/api/tracks",async(t,e)=>{let n=await st(),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>bx){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,bx).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:Bi,agentAvailability:await Cd(),tickets:await Za()})});x.get("/api/cost",async(t,e)=>{try{let n=await st();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await Yk(n);if(r.cursorUsage=ad(),t.query.format==="text")return e.type("text/plain").send(Zk(r));e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/roundup",async(t,e)=>{try{e.json(await e0(await st(),{window:t.query.window,from:t.query.from,to:t.query.to}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/spend",async(t,e)=>{try{let n=await a0(await st(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=ad(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=hs(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json(hs())});x.post("/api/usage/caps",async(t,e)=>{try{e.json(await pi(t.body?.caps??{},{source:"local"}))}catch(n){e.status(400).json({error:n?.message||String(n)})}});x.get("/api/usage/limits",async(t,e)=>{try{let n=t.query.fresh==="1"||t.query.fresh==="true";e.json(await w0(await st(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs",async(t,e)=>{try{e.json(await ng(await st()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs/:key",async(t,e)=>{try{let n=await rg(await st(),t.params.key);if(!n)return e.status(404).json({error:"unknown arc"});e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/arcs/backfill-beats",async(t,e)=>{try{e.json({ok:!0,...await kk()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/hook",async(t,e)=>{try{let{mode:n,target:r}=await $f(t.get(Qs),{byToken:Jb,legacy:()=>Ai(t.body?.session_id,t.body?.cwd)});if(n==="refused")return ta("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});n==="token"&&t.body?.session_id&&r.box?.sessionId&&t.body.session_id!==r.box.sessionId&&ta("mismatch",{track:r.track.id,box:r.box.name}),ta(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await Hk(t.body||{},()=>r);e.json({ok:!0,events:o.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.post("/api/telemetry",async(t,e)=>{try{if(!tx(To.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await Q0(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.get("/api/repos",async(t,e)=>{try{let n=await Oe(),r=new Set((await Promise.all(n.map(a=>et(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Ro()).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)})}});x.get("/api/github/token",async(t,e)=>{try{e.json(await oa(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/github/status",(t,e)=>{e.json({configured:ra(),calls:Oy(),latency:nb()})});x.post("/api/github/refresh",(t,e)=>{Ff(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await y0(String(t.query.q||""),{max:Number(t.query.max)||10});e.json({messages:n})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/gmail/messages/:id",async(t,e)=>{try{e.json({message:await b0(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function _f(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 mc({repo:t=null,number:e=null,branch:n=null}){let r=t?await et(t):null;if(r&&e!=null){let s=await Bt(r,e),a=s&&await Q(s.trackId);if(a)return a}return n?(await st()).find(s=>s.workspace?.branch===n&&_f(s,t,r))??null:null}async function Ex({repo:t=null,number:e=null,branch:n=null}){let r=await mc({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await et(t).catch(()=>null):null;return(await st()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&_f(a,t,o))??null}async function de(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await mc({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await et(s).catch(()=>null):null);await $({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,...l?{repo:l}:{},...i}}).catch(()=>{})}async function QP(t){let e=(await st()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await et(n.path);if(r)for(let o of n.prs){if(await Bt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&_f(a,n.path,r));s&&await Jt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}x.get("/api/git/prs",async(t,e)=>{try{let n=await lb();await QP(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=t.body||{};try{if(s){let h=o||"merge";if(!vs.has(h))throw new Error(`invalid merge method: ${h}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let k=await et((await Xe(n)).path);if(!k)throw new Error("repo has no GitHub remote");let v=await mc({repo:n,number:y,branch:l}).catch(()=>null);if(!v)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await Ss(n,y),await Jt({repo:k,number:y,trackId:v.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ls(k,y,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await rr(k,y,i===!0),await us(k,y,c===!0&&i!==!0),Re(),await de("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:v,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:y,auto:!0})}let d=await oo(n,r,{method:o||"squash"});await de("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await et(n).catch(()=>null),m=!1;if(p&&(i===!0&&await rr(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await us(p,d.number,!0).catch(()=>{}),await Rn(p,d.number).catch(()=>{}),m=await In(p,d.number).catch(()=>!1),await vn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let h=await mc({repo:n,number:d.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await mo(h.id).catch(()=>{})}e.json(d)}catch(d){let p=d?.message||String(d),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;e.status(m).json({error:p})}});x.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 et((await Xe(n)).path).catch(()=>null),l=c?await ds(c,i).catch(()=>null):null;try{await pd(n,i)}catch(u){if(!l)throw u}Re(),await de("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})}});x.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await et(n).catch(()=>null),s=await pb(n,{method:r||"merge",onMerged:a=>{de("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Rn(o,a.number).catch(()=>{}).then(()=>In(o,a.number)).catch(()=>{}).then(()=>vn(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})}});x.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:fb()})});x.post("/api/git/prs/merge-track",async(t,e)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=t.body||{};try{let i=a||"merge";if(!vs.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return e.status(400).json({error:"trackId required"});let c=await Q(n);if(!c)return e.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Ee()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return e.status(409).json({error:"no open PRs on this Track"});for(let d of l)await rr(d.repo,d.number,o===!0).catch(()=>{}),await us(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let p={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(p);try{let m=await fr(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let h=await ro(m,d.number);f.push({r:p,l:d,repoPath:m,pr:h,plan:ub(h,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:d,l:p,repoPath:m,pr:h,plan:y}of db(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await oo(m,p.number,{method:i}),await de("merged",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c}),await Rn(p.repo,p.number).catch(()=>{}),await In(p.repo,p.number).catch(()=>{}),await vn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await Ss(m,p.number).catch(()=>{}),d.action="updated-armed"),!await ls(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await de("auto-armed",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){d.ok=!1,d.action="failed",d.reason=k?.message||String(k)}if(Re(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await rr(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await us(d.repo,d.number,!1).catch(()=>{});return e.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}e.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/disarm-track",async(t,e)=>{let{trackId:n}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let r=await Q(n);if(!r)return e.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Ee()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ds(a.repo,a.number).catch(()=>null),c=await fr(a.repo).catch(()=>null);c&&await pd(c,a.number).catch(()=>{}),i&&(await de("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}Re(),e.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let i=await Q(n);if(!i)return e.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=qt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(h=>{let y=u.find(k=>k.name===h||k.repo===h||k.base===h||k.dir===h);if(!y)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return y});else if(f=await bs(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let h of f)try{let y=await hd(h.dir,{title:r,body:o,base:s}),k=h.repo||await et(h.dir).catch(()=>null);k&&y.number&&await Jt({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await de("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??h.name}}),p.push({repo:h.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:h.name,slug:h.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(h=>h.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(h=>`${h.repo}: ${h.error}`).join("; "),results:p})}let c=await hd(i.cwd,{title:r,body:o,base:s}),l=await et(i.cwd).catch(()=>null);l&&c.number&&await Jt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await de("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),e.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;e.status(l).json({error:c})}});x.get("/api/tracks/:id/repos",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await bs(n),o=new Map(r.map(a=>[a.base,a])),s=qt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});e.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/repos",async(t,e)=>{let{repo:n}=t.body||{};try{if(!n||typeof n!="string")return e.status(400).json({error:"repo (path or owner/name) required"});let r=await dk(t.params.id,n);e.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.delete("/api/tracks/:id/repos/:name",async(t,e)=>{try{let n=await fk(t.params.id,t.params.name);e.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;e.status(o).json({error:r})}});x.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await ro(n,r),s=await et(n).catch(()=>null),a=await Ex({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=qt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await mb(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ft.join("ci",i.name):"ci",f=ft.join(a.cwd,".bostrat",u),d=ft.join(".bostrat",u,`pr-${o.number}-fail.log`);await Gt.promises.mkdir(f,{recursive:!0}),await Gt.promises.writeFile(ft.join(a.cwd,d),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
437
+ `),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 z0(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}.${QN(n.host)}/`}function QN(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 K0(e)&&Jl(n[0])?n.slice(1).join("."):e}var tP=1e4,eP=(...t)=>fetch(...t);function nP(t=process.env){return!!(Dt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function J0({env:t=process.env}={}){if(!nP(t))return{loops:[],source:"unconfigured"};let e=new AbortController,n=setTimeout(()=>e.abort(),tP);try{let r=await eP(`${Dt(t)}/v1/loops`,{headers:{authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},signal:e.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}var Y0=10,X0=200,rP=120,oP=24,sP=/^\d{4}-\d{2}-\d{2}$/;function yf(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var aP=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function Z0(t,e=Date.now()){if(!t)return e-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(t).trim());if(n)return e-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(t);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${t}" \u2014 use 24h, 7d, or an ISO date`)}var iP=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function cP(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=ss(t);if(e.length)throw new Error(`changelog entry appears to contain a secret VALUE (${e.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of t.split(`
438
+ `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,rP)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${oP}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=iP.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);if(i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`,i.length>X0)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${X0}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>Y0)throw new Error(`${r.length} bullets \u2014 over the ${Y0}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function Q0(t,{repos:e=yf()}={}){let n=new Set(e);return(await $e()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=t&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,mergedAt:r.resolvedAt??null}))}async function tx(t,{md:e,day:n=null}){let r=cP(e),o=n==null||n===""?aP():String(n);if(!sP.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await I({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}import{execFile as lP}from"node:child_process";import{promisify as uP}from"node:util";var kf=uP(lP),ex=3e4,dP=30*6e4,fP=ex,pP=10*6e4,mP=10080*6e4,hP=60*6e4,wP=8,gP=1440*6e4,yP=3,nx="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",bf=60*6e4,bP=15*6e4,dc=new Map,fc=new Map,kP=/could not resolve|not found|no pull requests?/i,xP=t=>kP.test(String(t?.message||t||""));function SP(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<bf)}var rx={fast:fP,slow:pP,decay:hP};function ox(t,e){return t.armedAt&&e-t.armedAt<dP||SP(t,e)?"fast":e-(t.firstSeen||0)>mP?"decay":"slow"}function vP(t,e){return rx[ox(t,e)]}function Xs(t,e,n,r){let o=t.lanes?.get(G(e.repo,e.number))||{};return Mi({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function TP(t,e){Ye("watch");try{let{stdout:n}=await kf("gh",["pr","view",String(e),"--repo",t,"--json",nx],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await we({slug:t})}});return JSON.parse(n||"null")}catch(n){throw _n(n)&&En(n),n}}async function AP(t){Ye("watch");try{let{stdout:e}=await kf("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",nx],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await we({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw _n(e)&&En(e),e}}async function _P(){let t=new Map;for(let e of await nt().catch(()=>[]))t.set(e.id,e);return t}async function EP(t,e){await I({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 IP(t,e){let n=await pr(t);return n?so(n,e):null}async function RP(t,e,n){let r=await pr(t);if(!r)throw new Error(`no local checkout for ${t}`);return ao(r,e,{method:n})}async function $P(t,e){let n=await pr(t);if(!n)throw new Error(`no local checkout for ${t}`);return Es(n,e)}async function OP(t,e){await I({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 vo(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 CP=10*6e4;async function MP(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>gP){await vo(t,null,e,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(t.repo,t.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await vo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await vo(t,o,e,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(t.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<CP)return;await vo(t,o,e,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(t.repo,t.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(t.repo,t.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await vo(t,o,e,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(t.repo,t.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await vo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await sx(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function zs(t,e,n){await I({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 NP(t,e){try{return await kf("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Js(t,e,n,r=()=>!0){return(await P(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var Ys=(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 PP(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 oi(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await ai(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait),l=!!t.baseFoldAt&&o-t.baseFoldAt<bP;await og(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await rg(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Js(a,"pushed",t.number,f=>f.payload?.sha===s)&&await zs(a,"pushed",{...Ys(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>bf)await si(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 si(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Xs(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Js(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await zs(a,"ci-fixed",{...Ys(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await si(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>bf)await ii(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await ii(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Xs(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Js(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await zs(a,"reconciled",{...Ys(t,e),base:i.baseRefName||null}).catch(()=>{}),await ii(t.repo,t.number).catch(()=>{})}}}}async function DP(t,e,n,r){let o=Xs(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Js(s,"closed",t.number)&&await zs(s,"closed",{...Ys(t,e),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(t.repo,t.number,"closed").catch(()=>{}),await r.mark(t.repo,t.number,"closed").catch(()=>{})}async function BP(t,e,n){Xs(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Js(r,"lost",t.number)&&await zs(r,"lost",Ys(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function sx(t,e,n,r,o={}){let s=Xs(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await P(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(t.number))||await r.emitMerged(a,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(t.repo,t.number).catch(()=>{}),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function jP(t={}){let{now:e=Date.now(),auth:n=yd,view:r=TP,list:o=AP,links:s=ag,trackMap:a=_P,emitMerged:i=EP,mark:c=vn,archiveIfArmed:l=Rn,notifyIfArmed:u=$n,notifyIfResolved:f=Gd,batchThreshold:d=yP,detail:p=IP,merge:m=RP,updateBranch:h=$P,lookup:g=Lt,disarm:k=hs,emitDisarmed:S=OP,localHasCommit:R=NP}=t;if(!(await n().catch(()=>null))?.authenticated||Ti()>0)return 0;let O=(await s().catch(()=>[])).filter(T=>e-(dc.get(G(T.repo,T.number))||0)>=vP(T,e));if(!O.length)return 0;let N=Date.now(),A=new Map,C=new Map;for(let T of O){let L=ox(T,e);A.set(G(T.repo,T.number),{lane:L,intervalMs:rx[L]}),dc.set(G(T.repo,T.number),e),C.has(T.repo)||C.set(T.repo,[]),C.get(T.repo).push(T)}let F=await a().catch(()=>new Map),ct={now:e,lanes:A,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:h,lookup:g,disarm:k,emitDisarmed:S,localHasCommit:R};for(let[T,L]of C){let _t,b=new Set;if(L.length>d)_t=await o(T).catch(()=>null);else{_t=new Map;for(let w of L)try{let v=await r(T,w.number);v?.number!=null&&_t.set(Number(v.number),v)}catch(v){xP(v)&&b.add(G(w.repo,w.number))}}if(_t)for(let w of L){let v=G(w.repo,w.number),E=_t.get(Number(w.number));if(!E){if(b.has(v)){let pt=(fc.get(v)||0)+1;fc.set(v,pt),pt>=wP&&(await BP(w,F,ct),fc.delete(v),dc.delete(v))}continue}fc.delete(v),E.state==="MERGED"||E.state==="CLOSED"?(E.state==="MERGED"?await sx(w,E,F,ct):await DP(w,E,F,ct),dc.delete(v)):(await PP(w,E,F,ct).catch(()=>{}),w.autoMerge&&await MP(w,F,ct).catch(()=>{}))}}let Y={};for(let{lane:T}of A.values())Y[T]=(Y[T]||0)+1;return console.log(`[pr-watch] probed ${O.length} link(s) (${Object.entries(Y).map(([T,L])=>`${T} ${L}`).join(", ")}) in ${Date.now()-N}ms`),O.length}function ax(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await jP()}catch{}finally{t=!1}}},ex)}import{randomUUID as LP}from"node:crypto";var FP=2e4,ix=3,To=new Map,xf=new Set;async function cx(t,e,n){let r=await n(t).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===e?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function UP(t={}){let{sessions:e=null,tracks:n=null,append:r=I,read:o=P,now:s=Date.now()}=t;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=e??await ae(),i=n??await nt()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(u.archivedAt||!u.leadBox)continue;let f=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!f||f.agent!=="claude"||f.state!=="live")continue;let d=c.get(f.name);if(d)if(l.add(f.name),d.alternateOn){let p=(To.get(f.name)||0)+1;To.set(f.name,p),p===ix&&(xf.add(f.name),await cx(u,f.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:LP(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(To.get(f.name)||0)>=ix;if(To.delete(f.name),p||!xf.has(f.name)){xf.add(f.name);let m=await cx(u,f.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...To.keys()])l.has(u)||To.delete(u)}var Zs=null,Sf=!1;function lx(){return Zs?()=>{}:(Zs=setInterval(()=>{Sf||(Sf=!0,UP().catch(()=>{}).finally(()=>{Sf=!1}))},FP),Zs.unref?.(),()=>{clearInterval(Zs),Zs=null})}import rn from"node:fs/promises";import Ao from"node:path";import wx from"node:os";import ux from"node:fs/promises";import pc from"node:path";import{execFile as HP}from"node:child_process";import{promisify as WP}from"node:util";import{fileURLToPath as qP}from"node:url";var KP=WP(HP),fx=pc.dirname(qP(import.meta.url)),GP=pc.resolve(fx,"..","..","client","dist"),VP="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",zP=new Map([...VP].map((t,e)=>[t,e]));function JP(t){let e=[],n=0,r=0;for(let o of t){let s=zP.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 YP(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=JP(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 XP(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 dx=new Map;async function ZP(t){let e;try{e=await ux.stat(t)}catch{return null}let n=dx.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await ux.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:YP(r.mappings||"")};return dx.set(t,o),o}catch{return null}}var vf=null;function QP(){return vf||(vf=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):KP("git",["rev-parse","--short","HEAD"],{cwd:pc.resolve(fx,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),vf}var tD=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function eD(t,{distDir:e=GP}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(tD)){let[a,i,c,l]=s,u=await ZP(pc.join(e,"assets",`${i}.map`)),f=u?XP(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[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 px(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await QP();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await eD(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var nD=()=>process.env.BOSTRAT_STATE_DIR||Ao.join(process.env.HOME||wx.tmpdir(),".bostrat"),Fn=()=>Ao.join(nD(),"telemetry"),gx=()=>Ao.join(Fn(),".relay-offsets.json"),rD=50,oD=14,sD=3e4,mx=500,Qt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function aD(t,e){if(!t||typeof t!="object")return null;let n=Qt(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:Qt(t.anonId,64),sessionId:Qt(t.sessionId,64),userId:Qt(t.userId,64),release:Qt(t.release,64),appVersion:Qt(t.appVersion,32),route:Qt(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:Qt(t.error.kind,256),message:Qt(t.error.message,8192),stack:Qt(t.error.stack,32768),fingerprint:Qt(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:Qt(s?.n,64),r:Qt(s?.r,128)}))),o}var yx=t=>new Date(t).toISOString().slice(0,10),hx=0;async function bx(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,rD):[]).map(s=>aD(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await px(s);await rn.mkdir(Fn(),{recursive:!0});let o=Ao.join(Fn(),`${yx(e)}.ndjson`);return await rn.appendFile(o,r.map(s=>JSON.stringify(s)+`
439
+ `).join(""),"utf8"),e-hx>6*3600*1e3&&(hx=e,iD(e).catch(()=>{})),r.length}async function iD(t=Date.now()){let e=yx(t-oD*24*3600*1e3),n=[];try{n=await rn.readdir(Fn())}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 rn.rm(Ao.join(Fn(),s),{force:!0});let o=await xx();for(let s of r)delete o[s];await Sx(o)}var mc=new Map;function kx(t,e=Date.now()){let n=t||"unknown",r=mc.get(n);return r||(mc.size>512&&mc.clear(),r={tokens:40,at:e},mc.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 xx(){try{return JSON.parse(await rn.readFile(gx(),"utf8"))||{}}catch{return{}}}async function Sx(t){await rn.mkdir(Fn(),{recursive:!0}),await rn.writeFile(gx(),JSON.stringify(t),"utf8")}function cD(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||wx.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 lD({fetchImpl:t=fetch}={}){let e=await Aw();if(!e)return{sent:0,disabled:!0};let n;try{n=(await rn.readdir(Fn())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await xx(),o=0;for(let s of n){let a=Ao.join(Fn(),s),i=r[s]||0,c;try{let d=await rn.open(a,"r");try{let p=(await d.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
440
+ `).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=mx){let p=f.slice(d,d+mx).map(h=>cD(h)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await Sx(r)}return{sent:o,disabled:!1}}var Tf=null,Af=!1;function vx(){Tf||(Tf=setInterval(async()=>{if(!Af){Af=!0;try{await lD()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Af=!1}}},sD),Tf.unref?.())}import wc from"node:fs/promises";import Ix from"node:path";import uD from"node:os";var Rx=()=>process.env.BOSTRAT_STATE_DIR||Ix.join(process.env.HOME||uD.tmpdir(),".bostrat"),$x=()=>Ix.join(Rx(),"dictation-glossary.json"),dD=100,fD=500,pD=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],gc=null,_f=null,Ef=Promise.resolve();function Tx(){let t={},e=Date.now();for(let n of pD)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function Ox(){return gc||(_f??=wc.readFile($x(),"utf8").then(t=>JSON.parse(t)?.terms??Tx()).catch(()=>Tx()).then(t=>gc=t).finally(()=>_f=null),_f)}async function Ax(){let t=JSON.stringify({terms:gc},null,2),e=$x(),n=`${e}.${process.pid}.tmp`;await wc.mkdir(Rx(),{recursive:!0}),await wc.writeFile(n,t),await wc.rename(n,e)}function mD(){return Ef=Ef.then(Ax,Ax),Ef.catch(()=>{})}async function If(){return Object.values(await Ox()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var hD=t=>Math.max(1,Math.ceil(t.length/4));async function wD(){let t=[],e=0;for(let{term:n}of await If()){let r=hD(n);if(!(e+r>fD)&&(t.push(n),e+=r,t.length>=dD))break}return t}async function gD(t,e){let n=await Ox(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var _x=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),hc=t=>t.toLowerCase();function Ex(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Cx(t,e){let n=_x(t),r=_x(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 f=s-1;f>=0;f--)a[u][f]=hc(n[u])===hc(r[f])?a[u+1][f+1]+1:Math.max(a[u+1][f],a[u][f+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)hc(n[c])===hc(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(Ex(r[l])&&i.add(r[l]),l++);for(;l<s;l++)Ex(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await gD(u,"learned");return await mD(),[...i]}var Qs=null;function yD(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 Mx(){if(!Qs)return null;let t=await is();if(!t||t.apiKey.slice(-6)!==Qs.keyTail)return null;let{keyTail:e,...n}=Qs;return n}async function Nx(t,e){let n=await is();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 wD())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=yD(o.status,i);c.persistent&&(Qs={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 Qs=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var Rf={"gpt-realtime-2.1":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime":{textIn:4,textInCached:.4,textOut:16,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2.1-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20},"gpt-realtime-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20}};function bD(t){if(!t)return null;let e=String(t).trim();if(Rf[e])return e;let n=null;for(let r of Object.keys(Rf))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function kD(t,e={}){let n=Rf[bD(t)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(e[o])||0)*n[o];return r/1e6}function xD(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.error?.code||n?.error?.type||"";return t===429&&/insufficient_quota/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"The OpenAI account is out of credit \u2014 voice chat is down until you top up at platform.openai.com or drop a new key into ~/.bostrat/integrations.json."}:t===401||t===403?{status:502,code:"key_invalid",persistent:!0,message:"OpenAI rejected the API key (revoked, or missing Realtime access) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"mint_failed",persistent:!1,message:`OpenAI realtime session mint failed (${t}): ${e.slice(0,300)}`}}var ta=null;async function SD(){if(!ta)return null;let t=await Qa();if(!t||t.apiKey.slice(-6)!==ta.keyTail)return null;let{keyTail:e,...n}=ta;return n}async function vD(t){let e=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${t.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:t.model,audio:{output:{voice:t.voice}}}})});if(!e.ok){let o=await e.text().catch(()=>""),s=xD(e.status,o);s.persistent&&(ta={code:s.code,message:s.message,at:Date.now(),keyTail:t.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}ta=null;let n=await e.json(),r=n?.value??n?.client_secret?.value;if(!r){let o=new Error("OpenAI returned no client secret");throw o.status=502,o.code="mint_failed",o}return{clientSecret:{value:r,expiresAt:n?.expires_at??n?.client_secret?.expires_at??null}}}var TD={openai:vD};async function Px(){let t=await Qa();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await SD()}:{configured:!1}}async function Dx(){let t=await Qa();if(!t){let n=new Error("Voice chat is not configured \u2014 add openai.apiKey to ~/.bostrat/integrations.json");throw n.status=503,n.code="not_configured",n}let e=await TD[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function Bx(t,{model:e,usage:n={},seconds:r}={}){let o=c=>Math.max(0,Math.round(Number(c)||0)),s={textIn:o(n.textIn),textInCached:o(n.textInCached),textOut:o(n.textOut),audioIn:o(n.audioIn),audioInCached:o(n.audioInCached),audioOut:o(n.audioOut)},a=kD(e,s),i=await I(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var Wx=ft.dirname(_D(import.meta.url)),jx=Number(process.env.PORT)||3362,yc=s0(process.env),qx=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,_o=B0(process.env.BOSTRAT_ALLOW_IPS),Nf=j0(process.env.BOSTRAT_VIEW_FRONT_CIDRS,uc),Kx=F0(),Gx=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=me();x.disable("x-powered-by");x.use((t,e,n)=>{if(Vs(t)||_o.allowed(_o.clientIp(t))||Nf.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(G0);x.use(W0({token:Kx,enforce:Gx}));x.use("/api/mocks",me.json({limit:"1mb"}));x.use("/api/mocks",me.raw({type:"image/png",limit:"24mb"}));x.use("/api/mocks",me.raw({type:["video/mp4","video/webm"],limit:"105mb"}));x.use("/api/runbooks",me.json({limit:"1mb"}));x.use("/api/telemetry",me.json({limit:"256kb"}));x.use("/api/hook",me.json({limit:"2mb"}));x.use("/api/dictation",me.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",me.raw({type:()=>!0,limit:"25mb"}));x.use(me.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Yt,hostTmuxAlive:await dn(),autoTriggerPct:qx})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await ae()})});x.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 Kn(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})}});x.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!gt(n))return e.status(400).json({error:"invalid session name"});if(!await et(n))return e.status(404).json({error:"session not found"});try{await ye(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});x.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!gt(n))return e.status(400).json({error:"invalid session name"});let r=await et(n);if(!r)return e.status(404).json({error:"session not found"});let o=await Uo(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});x.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!gt(n))return e.status(400).json({error:"invalid session name"});try{let r=await ph(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)})}});x.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=Fa(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})});var Lx=10;x.get("/api/idle-guard",async(t,e)=>{try{e.json(zk({tracks:await nt(),views:await os(),queuedOf:n=>Cs(n)}))}catch{e.status(500).json({busy:!0,reasons:["error"]})}});x.get("/api/tracks",async(t,e)=>{let n=await nt(),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>Lx){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,Lx).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:Ui,agentAvailability:await Fd(),tickets:await ei()})});x.get("/api/cost",async(t,e)=>{try{let n=await nt();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await p0(n);if(r.cursorUsage=md(),t.query.format==="text")return e.type("text/plain").send(m0(r));e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/roundup",async(t,e)=>{try{e.json(await g0(await nt(),{window:t.query.window,from:t.query.from,to:t.query.to}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/spend",async(t,e)=>{try{let n=await S0(await nt(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=md(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=bs(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json(bs())});x.post("/api/usage/caps",async(t,e)=>{try{e.json(await gi(t.body?.caps??{},{source:"local"}))}catch(n){e.status(400).json({error:n?.message||String(n)})}});x.get("/api/usage/limits",async(t,e)=>{try{let n=t.query.fresh==="1"||t.query.fresh==="true";e.json(await C0(await nt(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs",async(t,e)=>{try{e.json(await fg(await nt()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs/:key",async(t,e)=>{try{let n=await pg(await nt(),t.params.key);if(!n)return e.status(404).json({error:"unknown arc"});e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/arcs/backfill-beats",async(t,e)=>{try{e.json({ok:!0,...await Nk()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/hook",async(t,e)=>{try{let{mode:n,target:r}=await Lf(t.get(sa),{byToken:lk,legacy:()=>Ri(t.body?.session_id,t.body?.cwd)});if(n==="refused")return aa("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});n==="token"&&t.body?.session_id&&r.box?.sessionId&&t.body.session_id!==r.box.sessionId&&aa("mismatch",{track:r.track.id,box:r.box.name}),aa(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await o0(t.body||{},()=>r);e.json({ok:!0,events:o.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.post("/api/telemetry",async(t,e)=>{try{if(!kx(_o.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await bx(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.get("/api/repos",async(t,e)=>{try{let n=await Ne(),r=new Set((await Promise.all(n.map(a=>rt(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Oo()).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)})}});x.get("/api/github/token",async(t,e)=>{try{e.json(await ua(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/github/status",(t,e)=>{e.json({configured:la(),calls:Ky(),latency:wb()})});x.post("/api/github/refresh",(t,e)=>{Jf(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await N0(String(t.query.q||""),{max:Number(t.query.max)||10});e.json({messages:n})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/gmail/messages/:id",async(t,e)=>{try{e.json({message:await P0(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function Pf(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 bc({repo:t=null,number:e=null,branch:n=null}){let r=t?await rt(t):null;if(r&&e!=null){let s=await Lt(r,e),a=s&&await Z(s.trackId);if(a)return a}return n?(await nt()).find(s=>s.workspace?.branch===n&&Pf(s,t,r))??null:null}async function Vx({repo:t=null,number:e=null,branch:n=null}){let r=await bc({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await rt(t).catch(()=>null):null;return(await nt()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&Pf(a,t,o))??null}async function pe(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await bc({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await rt(s).catch(()=>null):null);await I({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,...l?{repo:l}:{},...i}}).catch(()=>{})}async function $D(t){let e=(await nt()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await rt(n.path);if(r)for(let o of n.prs){if(await Lt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&Pf(a,n.path,r));s&&await Xt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}x.get("/api/git/prs",async(t,e)=>{try{let n=await vb();await $D(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=t.body||{};try{if(s){let h=o||"merge";if(!_s.has(h))throw new Error(`invalid merge method: ${h}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let k=await rt((await Xe(n)).path);if(!k)throw new Error("repo has no GitHub remote");let S=await bc({repo:n,number:g,branch:l}).catch(()=>null);if(!S)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await Es(n,g),await Xt({repo:k,number:g,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ps(k,g,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await or(k,g,i===!0),await ms(k,g,c===!0&&i!==!0),Ce(),await pe("auto-armed",{number:g,url:f,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:g,auto:!0})}let d=await ao(n,r,{method:o||"squash"});await pe("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await rt(n).catch(()=>null),m=!1;if(p&&(i===!0&&await or(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ms(p,d.number,!0).catch(()=>{}),await $n(p,d.number).catch(()=>{}),m=await Rn(p,d.number).catch(()=>!1),await vn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let h=await bc({repo:n,number:d.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await ho(h.id).catch(()=>{})}e.json(d)}catch(d){let p=d?.message||String(d),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;e.status(m).json({error:p})}});x.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 rt((await Xe(n)).path).catch(()=>null),l=c?await hs(c,i).catch(()=>null):null;try{await xd(n,i)}catch(u){if(!l)throw u}Ce(),await pe("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})}});x.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await rt(n).catch(()=>null),s=await Eb(n,{method:r||"merge",onMerged:a=>{pe("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&$n(o,a.number).catch(()=>{}).then(()=>Rn(o,a.number)).catch(()=>{}).then(()=>vn(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})}});x.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:_b()})});x.post("/api/git/prs/merge-track",async(t,e)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=t.body||{};try{let i=a||"merge";if(!_s.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return e.status(400).json({error:"trackId required"});let c=await Z(n);if(!c)return e.status(404).json({error:`Track ${n} isn't on this node`});let l=(await $e()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return e.status(409).json({error:"no open PRs on this Track"});for(let d of l)await or(d.repo,d.number,o===!0).catch(()=>{}),await ms(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let p={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(p);try{let m=await pr(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let h=await so(m,d.number);f.push({r:p,l:d,repoPath:m,pr:h,plan:Tb(h,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:d,l:p,repoPath:m,pr:h,plan:g}of Ab(f))try{if(g.action==="skip")d.action="skipped",d.reason=g.reason;else if(g.action==="merge")await ao(m,p.number,{method:i}),await pe("merged",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c}),await $n(p.repo,p.number).catch(()=>{}),await Rn(p.repo,p.number).catch(()=>{}),await vn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(g.action==="update-arm"&&(await Es(m,p.number).catch(()=>{}),d.action="updated-armed"),!await ps(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await pe("auto-armed",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){d.ok=!1,d.action="failed",d.reason=k?.message||String(k)}if(Ce(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await or(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await ms(d.repo,d.number,!1).catch(()=>{});return e.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}e.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/disarm-track",async(t,e)=>{let{trackId:n}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let r=await Z(n);if(!r)return e.status(404).json({error:`Track ${n} isn't on this node`});let o=(await $e()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await hs(a.repo,a.number).catch(()=>null),c=await pr(a.repo).catch(()=>null);c&&await xd(c,a.number).catch(()=>{}),i&&(await pe("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}Ce(),e.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let i=await Z(n);if(!i)return e.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Gt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(h=>{let g=u.find(k=>k.name===h||k.repo===h||k.base===h||k.dir===h);if(!g)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return g});else if(f=await vs(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let h of f)try{let g=await vd(h.dir,{title:r,body:o,base:s}),k=h.repo||await rt(h.dir).catch(()=>null);k&&g.number&&await Xt({repo:k,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await pe("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??h.name}}),p.push({repo:h.name,slug:k??null,ok:!0,url:g.url,number:g.number})}catch(g){p.push({repo:h.name,slug:h.repo??null,ok:!1,error:g?.message||String(g)})}let m=p.filter(h=>h.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(h=>`${h.repo}: ${h.error}`).join("; "),results:p})}let c=await vd(i.cwd,{title:r,body:o,base:s}),l=await rt(i.cwd).catch(()=>null);l&&c.number&&await Xt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await pe("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),e.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;e.status(l).json({error:c})}});x.get("/api/tracks/:id/repos",async(t,e)=>{try{let n=await Z(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await vs(n),o=new Map(r.map(a=>[a.base,a])),s=Gt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});e.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/repos",async(t,e)=>{let{repo:n}=t.body||{};try{if(!n||typeof n!="string")return e.status(400).json({error:"repo (path or owner/name) required"});let r=await Ak(t.params.id,n);e.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.delete("/api/tracks/:id/repos/:name",async(t,e)=>{try{let n=await _k(t.params.id,t.params.name);e.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;e.status(o).json({error:r})}});x.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await so(n,r),s=await rt(n).catch(()=>null),a=await Vx({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Gt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await Ib(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ft.join("ci",i.name):"ci",f=ft.join(a.cwd,".bostrat",u),d=ft.join(".bostrat",u,`pr-${o.number}-fail.log`);await zt.promises.mkdir(f,{recursive:!0}),await zt.promises.writeFile(ft.join(a.cwd,d),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
427
441
  ${k.log??"(no fetchable log \u2014 see link)"}
428
442
  `).join(`
429
- `));let p=l.map(k=>k.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",h=await ue(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${d} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await Jt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await qw(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await de("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),e.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(k=>k.name),log:d})}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})}});x.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await ro(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await et(n).catch(()=>null),a=await Ex({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=qt(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await ue(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} 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"}),u=s;u&&(await Jt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Kw(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await de("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:l.leadBox,queued:!!l.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})}});x.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=yy.filter(f=>t.body.actions.includes(f)),!o.length)return e.status(400).json({error:"actions must include commit, rebase, 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"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=o!=null&&t.body?.notify===!0,a=await Q(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await B(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return e.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let l=null;o&&(l=await ue(a.id,gi(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await $({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&nk(a.id,{actions:o}),e.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.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 zs=t=>e=>({...e,url:M0(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await Wa({trackId:t.query.track||void 0});e.json({views:n.map(zs(t)),maxLive:yn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await zh(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:zs(t)(n),session:jr(n.slug)})});x.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await Q(n):null;if(!i&&s&&(i=(await st()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await ql({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:zs(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})}});x.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:zs(t)(await Jh(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:zs(t)(await Xh(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})}});x.delete("/api/views/:id",async(t,e)=>{try{e.json(await Yh(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var tD=[["linear",Qn,Yl],["jira",Ja,Jl]],xf=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await zl(),r={};for(let[o,s,a]of tD){let i=await s();if(!i){xf.delete(o),r[o]={configured:!1};continue}let c=xf.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},xf.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)})}});x.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await zl()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await mw(o),i=n?await Q(n):null;!i&&r&&(i=(await st()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ft.sep)))||null),i&&await $({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})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await Q(n):null;if(!s&&r&&(s=(await st()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ft.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 Hr(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})}});x.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await Q(r):null;if(!o&&n&&(o=(await st()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ft.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 Rw(),ticket:await _e(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});x.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await _e(t.params.id)})});x.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Hr(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})}});x.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await lu(t.params.id)})});var wc=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;x.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await Cw(t.params.id))}catch(n){let r=n?.message||String(n);e.status(wc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await uu(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(wc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Nw(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(wc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await Q(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await Pw(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(wc(s)).json({error:s})}});x.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await Q(n):null;if(!s&&r&&(s=(await st()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ft.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 Bw(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})}});x.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await Dw()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});x.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await Q(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await jw(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})}});x.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Xn({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/mocks/:id",async(t,e)=>{let n=await Be(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});x.get("/api/mocks/:id/png",async(t,e)=>{let n=await Be(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ae(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Jo(n,r,"png");if(!o)return e.status(404).json({error:"no image for this version (poster extraction failed)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/media",async(t,e)=>{let n=await Be(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ae(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Jo(n,r,"video");if(!o)return e.status(404).json({error:"this version has no video (image or html sourced)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"video artifact missing"})})});x.get("/api/mocks/:id/html",async(t,e)=>{let n=await Be(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ae(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(ee(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});x.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&ja(n)?n:null,o=n&&!r?n:null,s=n?t.query:t.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await Q(a):null;if(!f&&c&&(f=(await st()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+ft.sep)))||null),!f)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(m=>m.name===f.leadBox),p=await Eh({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});e.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);e.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&ja(n)?n:null;try{let o=await Ih(t.params.id,{html:n?null:(t.body||{}).html,image:r,video:n&&!r?n:null,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;e.status(a).json({error:s})}});x.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Oh(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await ue(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})}});x.delete("/api/mocks/:id",async(t,e)=>{try{let n=(await Rb(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Mh(t.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/flows",async(t,e)=>{try{e.json({flows:await Tb({trackId:t.query.track||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/flows/:id",async(t,e)=>{let n=await Ab(t.params.id);if(!n)return e.status(404).json({error:"flow not found"});e.json({flow:n})});x.post("/api/flows",async(t,e)=>{let{trackId:n,name:r,steps:o,cwd:s}=t.body||{};try{let a=n?await Q(n):null;if(!a&&s&&(a=(await st()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.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 _b({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;e.status(c).json({error:i})}});x.post("/api/flows/:id/update",async(t,e)=>{try{let{steps:n,note:r}=t.body||{};e.json({ok:!0,flow:await Eb(t.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;e.status(o).json({error:r})}});x.delete("/api/flows/:id",async(t,e)=>{try{e.json(await Ib(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await st(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ft.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await Ga({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/runbooks/:id",async(t,e)=>{let n=await Gl(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});x.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Gl(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=nw(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(He(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});x.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await Q(n):null;if(!a&&s&&(a=(await st()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.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 ow({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})}});x.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await sw(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})}});x.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await aw(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})}});x.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await iw(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/changelog/draft",async(t,e)=>{try{let n=P0(t.query.since||null);e.json({since:n,repos:df(),prs:await D0(n)})}catch(n){let r=n?.message||String(n);e.status(/unparseable/i.test(r)?400:500).json({error:r})}});x.post("/api/changelog/publish",async(t,e)=>{let{md:n,day:r,trackId:o,cwd:s}=t.body||{};try{let a=o?await Q(o):null;if(!a&&s&&(a=(await st()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+ft.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await B0(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var eD=120,vf=400,hc=null;x.post("/api/notify",async(t,e)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=t.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return e.status(400).json({error:"body (the message) required"});if(i.length>vf)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${vf} cap; a notification is a nudge, put the detail in chat`});let c=es(`${n||""}
430
- ${i}`);if(c.length)return e.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await Q(a):null;if(!l&&s&&(l=(await st()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+ft.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),f=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),d={title:n?f(n,eD):l.name,body:f(i,vf),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await $(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),e.json({ok:!0,notification:d,delivered:hc?hc.status().connected:null})}catch(i){e.status(500).json({error:i?.message||String(i)})}});x.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 mx(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});x.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await rs(),terms:await bf(),problem:await px()})});x.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 fx(n,r)})});x.get("/api/tracks/:id/voice-chat",async(t,e)=>{if(!await Q(t.params.id))return e.status(404).json({error:"track not found"});e.json(await hx())});x.post("/api/tracks/:id/voice-chat/session",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});if(n.archivedAt)return e.status(409).json({error:"track is archived"});e.json(await wx())}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});x.post("/api/tracks/:id/voice-chat/usage",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=t.body||{};e.json({ok:!0,...await gx(n,{model:r,usage:o,seconds:s})})}catch(n){e.status(n?.status||500).json({error:n?.message||String(n)})}});x.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 jd({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Hr(l,c,{source:"create"})}catch(f){u=f?.message||String(f)}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})}});x.get("/api/tracks/:id",async(t,e)=>{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:Ix(Ns(t.params.id)),subtasks:ho(t.params.id).map(Js)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await yk(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await lu(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})}});x.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await mo(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})}});x.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await vk(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})}});x.get("/api/tracks/:id/messages",async(t,e)=>{let n=await Nd(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});x.get("/api/tracks/:id/events",async(t,e)=>{let n=await Q(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}
443
+ `));let p=l.map(k=>k.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",h=await fe(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${d} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Xt({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await tg(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await pe("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),e.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(k=>k.name),log:d})}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})}});x.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await so(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await rt(n).catch(()=>null),a=await Vx({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Gt(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await fe(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} 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"}),u=s;u&&(await Xt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await eg(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await pe("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:l.leadBox,queued:!!l.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})}});x.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=Cy.filter(f=>t.body.actions.includes(f)),!o.length)return e.status(400).json({error:"actions must include commit, rebase, 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"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=o!=null&&t.body?.notify===!0,a=await Z(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await P(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return e.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let l=null;o&&(l=await fe(a.id,xi(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await I({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&wk(a.id,{actions:o}),e.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.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 ea=t=>e=>({...e,url:z0(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await os({trackId:t.query.track||void 0});e.json({views:n.map(ea(t)),maxLive:bn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await nw(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:ea(t)(n),session:Fr(n.slug)})});x.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await Z(n):null;if(!i&&s&&(i=(await nt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Yl({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:ea(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})}});x.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:ea(t)(await rw(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:ea(t)(await ow(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})}});x.delete("/api/views/:id",async(t,e)=>{try{e.json(await sw(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var OD=[["linear",tr,ou],["jira",Za,nu]],$f=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await eu(),r={};for(let[o,s,a]of OD){let i=await s();if(!i){$f.delete(o),r[o]={configured:!1};continue}let c=$f.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},$f.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)})}});x.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await eu()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await Tw(o),i=n?await Z(n):null;!i&&r&&(i=(await nt()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ft.sep)))||null),i&&await I({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})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await Z(n):null;if(!s&&r&&(s=(await nt()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ft.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 qr(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})}});x.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await Z(r):null;if(!o&&n&&(o=(await nt()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ft.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 Lw(),ticket:await Re(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});x.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await Re(t.params.id)})});x.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await Z(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await qr(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})}});x.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await wu(t.params.id)})});var xc=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;x.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await Ww(t.params.id))}catch(n){let r=n?.message||String(n);e.status(xc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await gu(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(xc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await qw(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(xc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await Z(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await Kw(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(xc(s)).json({error:s})}});x.post("/api/tracks/:id/fullscreen-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{e.json(await jk(t.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;e.status(a).json({error:s})}});x.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await Z(n):null;if(!s&&r&&(s=(await nt()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ft.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 Vw(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})}});x.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await Gw()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});x.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await Z(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await zw(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})}});x.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Xn({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/mocks/:id",async(t,e)=>{let n=await Le(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});x.get("/api/mocks/:id/png",async(t,e)=>{let n=await Le(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ce(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Zo(n,r,"png");if(!o)return e.status(404).json({error:"no image for this version (poster extraction failed)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/media",async(t,e)=>{let n=await Le(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ce(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Zo(n,r,"video");if(!o)return e.status(404).json({error:"this version has no video (image or html sourced)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"video artifact missing"})})});x.get("/api/mocks/:id/html",async(t,e)=>{let n=await Le(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ce(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(ne(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});x.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&Wa(n)?n:null,o=n&&!r?n:null,s=n?t.query:t.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await Z(a):null;if(!f&&c&&(f=(await nt()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+ft.sep)))||null),!f)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(m=>m.name===f.leadBox),p=await Ph({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});e.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);e.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&Wa(n)?n:null;try{let o=await Dh(t.params.id,{html:n?null:(t.body||{}).html,image:r,video:n&&!r?n:null,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;e.status(a).json({error:s})}});x.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Lh(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await fe(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})}});x.delete("/api/mocks/:id",async(t,e)=>{try{let n=(await Wb(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Fh(t.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/flows",async(t,e)=>{try{e.json({flows:await jb({trackId:t.query.track||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/flows/:id",async(t,e)=>{let n=await Lb(t.params.id);if(!n)return e.status(404).json({error:"flow not found"});e.json({flow:n})});x.post("/api/flows",async(t,e)=>{let{trackId:n,name:r,steps:o,cwd:s}=t.body||{};try{let a=n?await Z(n):null;if(!a&&s&&(a=(await nt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.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 Fb({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;e.status(c).json({error:i})}});x.post("/api/flows/:id/update",async(t,e)=>{try{let{steps:n,note:r}=t.body||{};e.json({ok:!0,flow:await Ub(t.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;e.status(o).json({error:r})}});x.delete("/api/flows/:id",async(t,e)=>{try{e.json(await Hb(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let s=await nt(),a=String(t.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+ft.sep)))?.id,!n)return e.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await fw({trackId:n,status:t.query.status||void 0});e.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/runbooks/:id",async(t,e)=>{let n=await Ql(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});x.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Ql(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=uw(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});if(e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),n.remote){let o=await pw(n,r.v);return typeof o!="string"?e.status(404).json({error:"runbook artifact missing"}):e.send(o)}e.sendFile(qe(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});x.get("/api/loops",async(t,e)=>{e.json(await J0())});x.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await Z(n):null;if(!a&&s&&(a=(await nt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.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 mw({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})}});x.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await hw(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})}});x.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await ww(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})}});x.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await gw(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/changelog/draft",async(t,e)=>{try{let n=Z0(t.query.since||null);e.json({since:n,repos:yf(),prs:await Q0(n)})}catch(n){let r=n?.message||String(n);e.status(/unparseable/i.test(r)?400:500).json({error:r})}});x.post("/api/changelog/publish",async(t,e)=>{let{md:n,day:r,trackId:o,cwd:s}=t.body||{};try{let a=o?await Z(o):null;if(!a&&s&&(a=(await nt()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+ft.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await tx(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var CD=120,Of=400,kc=null;x.post("/api/notify",async(t,e)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=t.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return e.status(400).json({error:"body (the message) required"});if(i.length>Of)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Of} cap; a notification is a nudge, put the detail in chat`});let c=ss(`${n||""}
444
+ ${i}`);if(c.length)return e.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await Z(a):null;if(!l&&s&&(l=(await nt()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+ft.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),f=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),d={title:n?f(n,CD):l.name,body:f(i,Of),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await I(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),e.json({ok:!0,notification:d,delivered:kc?kc.status().connected:null})}catch(i){e.status(500).json({error:i?.message||String(i)})}});x.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 Nx(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});x.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await is(),terms:await If(),problem:await Mx()})});x.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 Cx(n,r)})});x.get("/api/tracks/:id/voice-chat",async(t,e)=>{if(!await Z(t.params.id))return e.status(404).json({error:"track not found"});e.json(await Px())});x.post("/api/tracks/:id/voice-chat/session",async(t,e)=>{try{let n=await Z(t.params.id);if(!n)return e.status(404).json({error:"track not found"});if(n.archivedAt)return e.status(409).json({error:"track is archived"});e.json(await Dx())}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});x.post("/api/tracks/:id/voice-chat/usage",async(t,e)=>{try{let n=await Z(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=t.body||{};e.json({ok:!0,...await Bx(n,{model:r,usage:o,seconds:s})})}catch(n){e.status(n?.status||500).json({error:n?.message||String(n)})}});x.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 Kd({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await qr(l,c,{source:"create"})}catch(f){u=f?.message||String(f)}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})}});x.get("/api/tracks/:id",async(t,e)=>{let n=await Z(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:zx(Ls(t.params.id)),subtasks:wo(t.params.id).map(na)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Ck(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await wu(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})}});x.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await ho(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})}});x.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await Dk(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})}});x.get("/api/tracks/:id/messages",async(t,e)=>{let n=await Ud(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});x.get("/api/tracks/:id/events",async(t,e)=>{let n=await Z(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}
431
445
  data: ${JSON.stringify(i)}
432
446
 
433
- `),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=am(n.id,r),a=setInterval(()=>e.write(`: hb
447
+ `),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await P(n))i.seq>o&&r(i);let s=hm(n.id,r),a=setInterval(()=>e.write(`: hb
434
448
 
435
- `),25e3);t.on("close",()=>{clearInterval(a),s()})});x.get("/api/tracks/:id/search",async(t,e)=>{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=String(t.query.q||"").trim();if(!r)return e.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(t.query.k)||8,1),50),s=Ho(await B(n)),a=_a(s,r,{k:o,role:t.query.role||null,tag:t.query.tag||null});e.json({q:r,indexed:s.N,hits:a.map(i=>({score:Number(i.score.toFixed(3)),seq:i.chunk.seq,ts:i.chunk.ts,role:i.chunk.role,box:i.chunk.boxId,provider:i.chunk.provider,tags:i.chunk.tags,text:i.chunk.text}))})});x.get("/api/memory",async(t,e)=>{e.json({stores:await tp()})});x.delete("/api/memory/:key/:file",async(t,e)=>{let n=String(t.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return e.status(400).json({error:"bad store key"});try{e.json({ok:!0,...await ep(n,String(t.params.file||""))})}catch(r){let o=r?.message||String(r);e.status(/not found/i.test(o)?404:/bad fact/i.test(o)?400:500).json({error:o})}});var nD=["Run a memory consolidation pass on this workspace's `.bostrat/memory/` directory:","1. Read `MEMORY.md` and every fact file.","2. Merge duplicates and facts that describe the same thing (keep the best-written one; fold unique details in; give the survivor a `supersedes:` entry naming what it absorbed, delete the absorbed file, and remove its index line).","3. Resolve contradictions in favor of the newest evidence \u2014 check the code where feasible before deciding.","4. Mark clearly-stale facts: correct them in place or delete them (file + index line).","5. Bump `metadata.modified` (ISO-8601, now) on every fact you touch.","6. Keep `MEMORY.md` one line per fact, under 200 lines.","When done, summarize what you merged, corrected, and deleted \u2014 that summary is the review record."].join(`
436
- `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await ue(t.params.id,nD)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});x.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await Tk(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})}});x.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await ue(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return e.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});x.post("/api/tracks/:id/upload",async(t,e)=>{try{let n=await Q(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 Pb(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})}});x.get("/api/tracks/:id/uploads/:file",async(t,e)=>{try{let n=await Q(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=ft.basename(String(t.params.file||""));if(!r||!vd(ft.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(ft.join(xd(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 rD=/^[0-9a-fA-F-]{8,64}$/;x.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await Q(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(!rD.test(o)||!Number.isInteger(s)||s<0)return e.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(d=>d.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=zP.createInterface({input:Gt.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let p;try{p=JSON.parse(d)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let f=l?Jc(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return e.status(404).json({error:"image not found"});e.set("content-type",f.mediaType),e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.send(Buffer.from(f.data,"base64"))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await Bd(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})}});x.delete("/api/tracks/:id/queue",async(t,e)=>{try{let{removed:n,restored:r,draftRev:o}=await Zb(t.params.id);e.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await Pd(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|no answer selected/i.test(a)?409:500;e.status(i).json({error:a})}});x.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await wk({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})}});x.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await Yb(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})}});x.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n,rev:r}=t.body||{};try{e.json(await ak(t.params.id,typeof n=="string"?n:"",{rev:typeof r=="number"?r:null}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required/i.test(s)?400:500;e.status(a).json({error:s})}});x.get("/api/tracks/:id/usage",async(t,e)=>{let n=await Q(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 nc(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await ik(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})}});x.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await Rk(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Js(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return e.status(429).json({error:"usage_cap",message:i,cap:a.usageCap});let 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})}});x.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:ho(t.params.id).map(Js)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await Xb(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});x.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:Js(qd(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});x.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:Js(Ok(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Js(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}x.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await Ud(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);if(s?.usageCap)return e.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});x.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await rk(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})}});x.get("/api/tracks/:id/handoff-lead",(t,e)=>{e.json(Ix(Ns(t.params.id))??{running:!1})});function Ix(t){return t?{running:!t.finishedAt,step:t.step,error:t.error,fromBox:t.fromBox,toBox:t.toBox,toAgent:t.toAgent,skipPermissions:t.skipPermissions,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}x.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:fa(),paths:yp,trace:await gp({limit:n})})});var Sf=ft.resolve(Sx,"..","..","client","dist");Gt.existsSync(Sf)&&(x.use(fe.static(Sf)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ft.join(Sf,"index.html"))));var Rx=XP.createServer(x),oD=new ZP({noServer:!0}),$x=(t,e,n)=>{if(!(Us(t)||To.allowed(To.clientIp(t))||Af.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
449
+ `),25e3);t.on("close",()=>{clearInterval(a),s()})});x.get("/api/tracks/:id/search",async(t,e)=>{let n=await Z(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=String(t.query.q||"").trim();if(!r)return e.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(t.query.k)||8,1),50),s=Ko(await P(n)),a=Oa(s,r,{k:o,role:t.query.role||null,tag:t.query.tag||null});e.json({q:r,indexed:s.N,hits:a.map(i=>({score:Number(i.score.toFixed(3)),seq:i.chunk.seq,ts:i.chunk.ts,role:i.chunk.role,box:i.chunk.boxId,provider:i.chunk.provider,tags:i.chunk.tags,text:i.chunk.text}))})});x.get("/api/memory",async(t,e)=>{e.json({stores:await lp()})});x.delete("/api/memory/:key/:file",async(t,e)=>{let n=String(t.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return e.status(400).json({error:"bad store key"});try{e.json({ok:!0,...await up(n,String(t.params.file||""))})}catch(r){let o=r?.message||String(r);e.status(/not found/i.test(o)?404:/bad fact/i.test(o)?400:500).json({error:o})}});var MD=["Run a memory consolidation pass on this workspace's `.bostrat/memory/` directory:","1. Read `MEMORY.md` and every fact file.","2. Merge duplicates and facts that describe the same thing (keep the best-written one; fold unique details in; give the survivor a `supersedes:` entry naming what it absorbed, delete the absorbed file, and remove its index line).","3. Resolve contradictions in favor of the newest evidence \u2014 check the code where feasible before deciding.","4. Mark clearly-stale facts: correct them in place or delete them (file + index line).","5. Bump `metadata.modified` (ISO-8601, now) on every fact you touch.","6. Keep `MEMORY.md` one line per fact, under 200 lines.","When done, summarize what you merged, corrected, and deleted \u2014 that summary is the review record."].join(`
450
+ `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await fe(t.params.id,MD)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});x.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await zd(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})}});x.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await fe(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return e.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});x.post("/api/tracks/:id/upload",async(t,e)=>{try{let n=await Z(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 Jb(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})}});x.get("/api/tracks/:id/uploads/:file",async(t,e)=>{try{let n=await Z(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=ft.basename(String(t.params.file||""));if(!r||!$d(ft.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(ft.join(Rd(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 ND=/^[0-9a-fA-F-]{8,64}$/;x.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await Z(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(!ND.test(o)||!Number.isInteger(s)||s<0)return e.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(d=>d.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return e.status(404).json({error:"image source not available"});let i=De(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=AD.createInterface({input:zt.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let p;try{p=JSON.parse(d)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let f=l?tl(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return e.status(404).json({error:"image not found"});e.set("content-type",f.mediaType),e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.send(Buffer.from(f.data,"base64"))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await qd(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})}});x.delete("/api/tracks/:id/queue",async(t,e)=>{try{let{removed:n,restored:r,draftRev:o}=await fk(t.params.id);e.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await Hd(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|no answer selected/i.test(a)?409:500;e.status(i).json({error:a})}});x.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await $k({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})}});x.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await dk(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})}});x.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n,rev:r}=t.body||{};try{e.json(await kk(t.params.id,typeof n=="string"?n:"",{rev:typeof r=="number"?r:null}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required/i.test(s)?400:500;e.status(a).json({error:s})}});x.get("/api/tracks/:id/usage",async(t,e)=>{let n=await Z(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 ic(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await xk(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})}});x.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await qk(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:na(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return e.status(429).json({error:"usage_cap",message:i,cap:a.usageCap});let 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})}});x.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:wo(t.params.id).map(na)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await uk(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});x.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:na(Xd(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});x.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:na(Gk(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function na(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}x.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await Jd(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);if(s?.usageCap)return e.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});x.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await gk(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})}});x.get("/api/tracks/:id/handoff-lead",(t,e)=>{e.json(zx(Ls(t.params.id))??{running:!1})});function zx(t){return t?{running:!t.finishedAt,step:t.step,error:t.error,fromBox:t.fromBox,toBox:t.toBox,toAgent:t.toAgent,skipPermissions:t.skipPermissions,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}x.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:ga(),paths:Ep,trace:await _p({limit:n})})});var Cf=ft.resolve(Wx,"..","..","client","dist");zt.existsSync(Cf)&&(x.use(me.static(Cf)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ft.join(Cf,"index.html"))));var Jx=ED.createServer(x),PD=new RD({noServer:!0}),Yx=(t,e,n)=>{if(!(Vs(t)||_o.allowed(_o.clientIp(t))||Nf.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
437
451
  \r
438
- `),e.destroy();return}if(O0(t,e,n))return;if(!I0(t,Ax,{enforce:_x})){e.write(`HTTP/1.1 401 Unauthorized\r
452
+ `),e.destroy();return}if(V0(t,e,n))return;if(!q0(t,Kx,{enforce:Gx})){e.write(`HTTP/1.1 401 Unauthorized\r
439
453
  \r
440
- `),e.destroy();return}let r=Op(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
454
+ `),e.destroy();return}let r=Fp(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
441
455
  \r
442
- `),e.destroy();return}let o=r.sessionName;if(!wt(o)){e.write(`HTTP/1.1 400 Bad Request\r
456
+ `),e.destroy();return}let o=r.sessionName;if(!gt(o)){e.write(`HTTP/1.1 400 Bad Request\r
443
457
  \r
444
- `),e.destroy();return}let{cols:s,rows:a}=r;oD.handleUpgrade(t,e,n,i=>Ip(i,o,{cols:s,rows:a}))};Rx.on("upgrade",$x);var Ox=process.env.BOSTRAT_CERT_DIR||ft.join(process.env.HOME||Tf.tmpdir(),".bostrat","certs"),kx=Number(process.env.BOSTRAT_TLS_PORT)||3443,xx=ft.join(Ox,"bostrat.globopi.key"),vx=ft.join(Ox,"bostrat.globopi.crt");if(Gt.existsSync(xx)&&Gt.existsSync(vx)){let t=YP.createServer({key:Gt.readFileSync(xx),cert:Gt.readFileSync(vx)},x);t.on("upgrade",$x),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(kx,pc,()=>{console.log(`[bostrat-agent] https listening on ${pc||"0.0.0.0"}:${kx}`)})}Rx.listen(yx,pc,()=>{console.log(`[bostrat-agent] listening on ${pc||"0.0.0.0"}:${yx}`),console.log(`[bostrat-agent] tmux socket: ${zt}`),console.log(`[bostrat-agent] auto-trigger default: ${Tx}%`),console.log(`[bostrat-agent] ip allowlist: ${To.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Af.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Ek().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Ik().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Sk().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)),hk().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)),cD(),iD(),Xg({listTracks:st,getTrack:Q}).then(t=>console.log(`[bostrat-agent] spend caps ${t.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(t.spent).length} provider counter(s)`)).catch(t=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",t?.message||t)),d0()&&f0({listTracks:st}),W0(),Lw({resolveTrack:Q}),Uk({listTracks:st,createTrack:jd,archiveTrack:mo,listRepos:Oe,resolveTrack:Q,sayToLead:ue,answerPrompt:Pd,startLeadHandoff:Ud,interruptLead:Bd,listSubtaskJobs:ho,cancelSubtask:qd,readEvents:B,appendEvent:$,knownAgents:Bi}),sD(),Do().then(t=>{t||console.warn(`[boot] no host tmux server on ${zt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),rx(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(hc=Gk({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Tf.hostname(),name:process.env.BOSTRAT_NODE_NAME||Tf.hostname(),listTracks:st,detectAgents:Cd,sampleMetrics:zk}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),vr().catch(()=>{}),uw().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Fw().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),Ph().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),Mb().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),Yw().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{hc?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function sD(){aD(),Ha().catch(()=>{}),setInterval(()=>Ha().catch(()=>{}),1e4)}function aD(){for(let t of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-runbook","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let e=ft.resolve(Sx,"..","bin",t),n=ft.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Gt.existsSync(e))continue;Gt.mkdirSync(n,{recursive:!0});let r=ft.join(n,t);(Gt.existsSync(r)?Gt.readlinkSync(r):null)!==e&&(Gt.rmSync(r,{force:!0}),Gt.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 iD(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await st(),r=await c0({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 cD(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await st();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await Nd(n.id).catch(()=>{}),await hb(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
458
+ `),e.destroy();return}let{cols:s,rows:a}=r;PD.handleUpgrade(t,e,n,i=>Bp(i,o,{cols:s,rows:a}))};Jx.on("upgrade",Yx);var Xx=process.env.BOSTRAT_CERT_DIR||ft.join(process.env.HOME||Mf.tmpdir(),".bostrat","certs"),Fx=Number(process.env.BOSTRAT_TLS_PORT)||3443,Ux=ft.join(Xx,"bostrat.globopi.key"),Hx=ft.join(Xx,"bostrat.globopi.crt");if(zt.existsSync(Ux)&&zt.existsSync(Hx)){let t=ID.createServer({key:zt.readFileSync(Ux),cert:zt.readFileSync(Hx)},x);t.on("upgrade",Yx),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(Fx,yc,()=>{console.log(`[bostrat-agent] https listening on ${yc||"0.0.0.0"}:${Fx}`)})}Jx.listen(jx,yc,()=>{console.log(`[bostrat-agent] listening on ${yc||"0.0.0.0"}:${jx}`),console.log(`[bostrat-agent] tmux socket: ${Yt}`),console.log(`[bostrat-agent] auto-trigger default: ${qx}%`),console.log(`[bostrat-agent] ip allowlist: ${_o.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Nf.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Uk().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Wk().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Bk().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)),Rk().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)),LD(),jD(),uy({listTracks:nt,getTrack:Z}).then(t=>console.log(`[bostrat-agent] spend caps ${t.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(t.spent).length} provider counter(s)`)).catch(t=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",t?.message||t)),E0()&&I0({listTracks:nt}),ax(),lx(),Jw({resolveTrack:Z}),r0({listTracks:nt,createTrack:Kd,archiveTrack:ho,listRepos:Ne,resolveTrack:Z,sayToLead:fe,answerPrompt:Hd,startLeadHandoff:Jd,interruptLead:qd,listSubtaskJobs:wo,cancelSubtask:Xd,readEvents:P,appendEvent:I,knownAgents:Ui}),DD(),Lo().then(t=>{t||console.warn(`[boot] no host tmux server on ${Yt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),vx(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(kc=c0({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Mf.hostname(),name:process.env.BOSTRAT_NODE_NAME||Mf.hostname(),listTracks:nt,detectAgents:Fd,sampleMetrics:u0}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),vr().catch(()=>{}),kw().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Yw().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),Wh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),Gb().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),ig().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{kc?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function DD(){BD(),Va().catch(()=>{}),setInterval(()=>Va().catch(()=>{}),1e4)}function BD(){for(let t of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-runbook","bostrat-loop","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let e=ft.resolve(Wx,"..","bin",t),n=ft.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!zt.existsSync(e))continue;zt.mkdirSync(n,{recursive:!0});let r=ft.join(n,t);(zt.existsSync(r)?zt.readlinkSync(r):null)!==e&&(zt.rmSync(r,{force:!0}),zt.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 jD(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await nt(),r=await T0({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 LD(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await nt();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await Ud(n.id).catch(()=>{}),await Rb(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}