bostrat-node 0.2.24 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,88 +1,88 @@
1
- var Px=Object.defineProperty;var Ef=(t,e)=>{for(var n in e)Px(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 rS}from"node:child_process";import{promisify as oS}from"node:util";import ma from"node:fs";import{randomUUID as sS}from"node:crypto";import Vt from"node:fs/promises";import Of from"node:os";import Lt from"node:path";import{execFile as Fx}from"node:child_process";import{promisify as Ux}from"node:util";import{createHash as Dx,randomBytes as Bx,timingSafeEqual as jx}from"node:crypto";var Zs="BOSTRAT_HOOK_TOKEN",Qs="x-bostrat-hook";function If(){return Bx(32).toString("hex")}function To(t){let e=typeof t=="string"?t.trim():"";return e?Dx("sha256").update(e).digest("hex"):null}function Rf(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:jx(Buffer.from(t),Buffer.from(e))}var yc=new Map,bc=new Map,Lx=6e4;function ta(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(yc.set(t,(yc.get(t)||0)+1),bc.has(t)&&n-bc.get(t)<Lx)return!1;bc.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=${yc.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 Hx(){return process.env.CLAUDE_CONFIG_DIR?Lt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Lt.join(Of.homedir(),".claude.json")}function Wx(){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=Hx(),r=Wx(),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 qx(e)}async function qx(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 Kx(e,["/.claude/settings.local.json"])}catch{}}async function Kx(t,e){try{let{stdout:n}=await Ux(Fx)("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 Ev=Object.defineProperty;var C=(t,e)=>()=>(t&&(e=t(t=0)),e);var dl=(t,e)=>{for(var n in e)Ev(t,n,{get:e[n],enumerable:!0})};import{createHash as _v,randomBytes as Iv,timingSafeEqual as Rv}from"node:crypto";function mm(){return Iv(32).toString("hex")}function zo(t){let e=typeof t=="string"?t.trim():"";return e?_v("sha256").update(e).digest("hex"):null}function hm(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:Rv(Buffer.from(t),Buffer.from(e))}function Jo(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(fl.set(t,(fl.get(t)||0)+1),pl.has(t)&&n-pl.get(t)<$v)return!1;pl.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=${fl.get(t)}${o?` ${o}`:""}`),!0}async function wm(t,{byToken:e,legacy:n,env:r=process.env}={}){let o=typeof t=="string"?t.trim():"";if(!o)return r.BOSTRAT_HOOK_LEGACY_DENY==="1"?{mode:"legacy-denied",target:null}:{mode:"legacy",target:await n()};let s=await e(o);return s?.track?{mode:"token",target:s}:{mode:"refused",target:null}}var Pa,Na,fl,pl,$v,Yo=C(()=>{Pa="BOSTRAT_HOOK_TOKEN",Na="x-bostrat-hook";fl=new Map,pl=new Map,$v=6e4});import Zt from"node:fs/promises";import gm from"node:os";import Vt from"node:path";import{execFile as Ov}from"node:child_process";import{promisify as Cv}from"node:util";function Da(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Mv(){return process.env.CLAUDE_CONFIG_DIR?Vt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Vt.join(gm.homedir(),".claude.json")}function Pv(){return process.env.CLAUDE_CONFIG_DIR?Vt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Vt.join(gm.homedir(),".claude","settings.json")}async function pn(t){if(!t||typeof t!="string")return;let e=Vt.resolve(t),n=Mv(),r=Pv(),o={};try{o=JSON.parse(await Zt.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 Zt.mkdir(Vt.dirname(n),{recursive:!0}),await Zt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Zt.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 "${Na}: $${Pa}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await Zt.mkdir(Vt.dirname(r),{recursive:!0}),await Zt.writeFile(r,JSON.stringify(i,null,2)),await Nv(e)}async function Nv(t){try{let e=Vt.resolve(t),n=Vt.join(e,".bostrat","memory","claude"),r=Vt.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await Zt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Vt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Zt.mkdir(n,{recursive:!0}),await Zt.mkdir(Vt.dirname(r),{recursive:!0}),await Zt.writeFile(r,JSON.stringify(o,null,2)),await Dv(e,["/.claude/settings.local.json"])}catch{}}async function Dv(t,e){try{let{stdout:n}=await Cv(Ov)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Vt.resolve(t,n.trim()),o=await Zt.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
2
+ `).includes(a));if(!s.length)return;await Zt.mkdir(Vt.dirname(r),{recursive:!0}),await Zt.appendFile(r,(o&&!o.endsWith(`
3
3
  `)?`
4
4
  `:"")+s.join(`
5
5
  `)+`
6
- `)}catch{}}import kc from"node:fs/promises";import Gx from"node:os";import na from"node:path";function Vx(){let t=process.env.CODEX_HOME||na.join(Gx.homedir(),".codex");return na.join(t,"config.toml")}function Mf(t){return`[projects."${t}"]`}function zx(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=Vx(),r="";try{r=await kc.readFile(n,"utf8")}catch{}if(zx(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{}}var Xo=C(()=>{Yo()});import ml from"node:fs/promises";import Bv from"node:os";import Ba from"node:path";function jv(){let t=process.env.CODEX_HOME||Ba.join(Bv.homedir(),".codex");return Ba.join(t,"config.toml")}function ym(t){return`[projects."${t}"]`}function Lv(t,e){let n=ym(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 Nr(t){if(!t||typeof t!="string")return;let e=Ba.resolve(t),n=jv(),r="";try{r=await ml.readFile(n,"utf8")}catch{}if(Lv(r,e))return;let o=ym(e);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
7
7
  trust_level = "trusted"
8
- `;r=r.slice(0,a)+f+r.slice(l)}else{let s=`
8
+ `;r=r.slice(0,a)+d+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 kc.mkdir(na.dirname(n),{recursive:!0}),await kc.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 Jx from"node:os";import Ao from"node:path";function Xx(t){let n=Ao.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ao.join(Jx.homedir(),".cursor");return Ao.join(r,"projects",n)}async function br(t){if(!t||typeof t!="string")return;let e=Ao.resolve(t),n=Xx(e),r=Ao.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 Sv from"node:os";import X from"node:path";import{execFile as Tv}from"node:child_process";import{promisify as Av}from"node:util";import iv from"node:fs";import Pf from"node:fs/promises";import Eo from"node:path";import cv from"node:os";import{fileURLToPath as lv}from"node:url";import{execFile as uv}from"node:child_process";import{promisify as dv}from"node:util";import vc from"node:fs/promises";import sn from"node:path";import{execFile as Yx}from"node:child_process";import{promisify as Zx}from"node:util";var Qx=Zx(Yx),Nf=process.env.HOME||"/home/globo",tv=sn.join(process.env.BOSTRAT_STATE_DIR||sn.join(Nf,".bostrat"),"repos"),ev=[...(process.env.BOSTRAT_REPOS_ROOT||sn.join(Nf,"Documents")).split(","),tv].map(t=>t.trim()).filter(Boolean),nv=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,rv=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function ov(t){try{return(await vc.stat(sn.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function sv(t){let e=i=>vc.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 ov(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:sn.basename(o),branch:await sv(o)});return}if(s>=nv)return;let i;try{i=await vc.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||rv.has(c.name)||await e(sn.join(o,c.name),s+1)}for(let o of ev)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 av(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var xc=new Map;async function et(t){if(!t)return null;if(xc.has(t))return xc.get(t);let e=null;try{let{stdout:n}=await Qx("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=av(n)}catch{e=null}return xc.set(t,e),e}async function _o(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=dv(uv),fv=process.env.BOSTRAT_STATE_DIR||Eo.join(process.env.HOME||cv.tmpdir(),".bostrat"),pv=Eo.join(fv,"repos"),Bf=/^[^/\s]+\/[^/\s]+$/,mv=5*6e4,hv=6e4,wv=10*6e4,jf=fetch,Sc=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=Sc.get(n);if(r&&r.expiresAt-Date.now()>mv)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 Sc.set(n,a),a}async function Io({env:t=process.env,force:e=!1}={}){if(!ra(t))return{configured:!1,repos:[],username:null};if(!e&&Fn&&Date.now()-Fn.at<hv)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(){Sc.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 Tc({env:t=process.env}={}){return(await Io({env:t})).username??null}var gv=()=>lv(new URL("../bin/bostrat-git-credential",import.meta.url)),yv=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function bv(t){let[e,n]=String(t).split("/");return Eo.join(pv,e,n)}async function Ac(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",`!"${gv()}" ${yv(e)}`),await n("config","credential.useHttpPath","true")}async function _c(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Bf.test(String(t||"")))throw kr(`bad repo slug "${t}"`,400);let r=bv(t);if(iv.existsSync(Eo.join(r,".git")))return r;let{token:o}=await oa(t,{env:e});await Pf.mkdir(Eo.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:wv,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 Ac(r,{env:e}),r}var kv=6e4,xv=5e3,Ec=new Map,vv=(...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(),xv);try{return await vv(`${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=Ec.get(t);if(!n&&r&&Date.now()-r.at<kv)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 Ec.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 Ec.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 _v=Av(Tv),Ic=process.env.BOSTRAT_STATE_DIR||X.join(process.env.HOME||Sv.tmpdir(),".bostrat"),Kf=200,Ev=25*1024;function aa(){return X.join(Ic,"memory","repos")}var Me="operator";function Un(t){return t===Me?X.join(Ic,"memory",Me):X.join(aa(),t)}var Jf=".tombstones.json";async function Ro(t){try{return JSON.parse(await L.readFile(X.join(t,Jf),"utf8"))}catch{return{}}}async function Rc(t,e){let n=await Ro(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 _v("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function $c(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 Oc(t,e,n,r=new Map){let o=(t||"").split(`
13
+ `:"")}try{await ml.mkdir(Ba.dirname(n),{recursive:!0}),await ml.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}var ja=C(()=>{});import bm from"node:fs/promises";import Fv from"node:os";import Zo from"node:path";function Uv(t){let n=Zo.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Zo.join(Fv.homedir(),".cursor");return Zo.join(r,"projects",n)}async function Dr(t){if(!t||typeof t!="string")return;let e=Zo.resolve(t),n=Uv(e),r=Zo.join(n,".workspace-trusted");await bm.mkdir(n,{recursive:!0}),await bm.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
+ `,"utf8")}var La=C(()=>{});import wl from"node:fs/promises";import mn from"node:path";import{execFile as Hv}from"node:child_process";import{promisify as Wv}from"node:util";async function Jv(t){try{return(await wl.stat(mn.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function Yv(t){let e=i=>wl.readFile(mn.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 Fe(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await Jv(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:mn.basename(o),branch:await Yv(o)});return}if(s>=Vv)return;let i;try{i=await wl.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||zv.has(c.name)||await e(mn.join(o,c.name),s+1)}for(let o of Gv)await e(mn.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 Xv(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}async function rt(t){if(!t)return null;if(hl.has(t))return hl.get(t);let e=null;try{let{stdout:n}=await qv("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=Xv(n)}catch{e=null}return hl.set(t,e),e}async function Qo(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await Fe())if((await rt(n.path))?.toLowerCase()===e)return n.path;return null}var qv,km,Kv,Gv,Vv,zv,hl,Zn=C(()=>{qv=Wv(Hv),km=process.env.HOME||"/home/globo",Kv=mn.join(process.env.BOSTRAT_STATE_DIR||mn.join(km,".bostrat"),"repos"),Gv=[...(process.env.BOSTRAT_REPOS_ROOT||mn.join(km,"Documents")).split(","),Kv].map(t=>t.trim()).filter(Boolean),Vv=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,zv=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);hl=new Map});import Zv from"node:fs";import xm from"node:fs/promises";import ts from"node:path";import Qv from"node:os";import{fileURLToPath as tT}from"node:url";import{execFile as eT}from"node:child_process";import{promisify as nT}from"node:util";function $t(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 Fa(t=process.env){return!!($t(t)&&t.BOSTRAT_PLANE_TOKEN)}async function Ua(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!vm.test(n))throw Br("repo must be owner/name",400);let r=gl.get(n);if(r&&r.expiresAt-Date.now()>sT)return r;if(!Fa(e))throw Br("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Tm(`${$t(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Am(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Br(`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 Br(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 gl.set(n,a),a}async function es({env:t=process.env,force:e=!1}={}){if(!Fa(t))return{configured:!1,repos:[],username:null};if(!e&&Qn&&Date.now()-Qn.at<aT)return Qn.data;try{let n=await Tm(`${$t(t)}/v1/github/repos`,{headers:Am(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Qn={at:Date.now(),data:r},r}catch(n){return Qn?Qn.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Em(){gl.clear(),Qn=null}async function Te({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 yl({env:t=process.env}={}){return(await es({env:t})).username??null}function uT(t){let[e,n]=String(t).split("/");return ts.join(oT,e,n)}async function bl(t,{env:e=process.env}={}){let n=(...r)=>Sm("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${cT()}" ${lT(e)}`),await n("config","credential.useHttpPath","true")}async function kl(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!vm.test(String(t||"")))throw Br(`bad repo slug "${t}"`,400);let r=uT(t);if(Zv.existsSync(ts.join(r,".git")))return r;let{token:o}=await Ua(t,{env:e});await xm.mkdir(ts.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Sm("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:iT,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await xm.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Br(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await bl(r,{env:e}),r}var Sm,rT,oT,vm,sT,aT,iT,Tm,gl,Qn,Am,Br,cT,lT,oe=C(()=>{Zn();Sm=nT(eT),rT=process.env.BOSTRAT_STATE_DIR||ts.join(process.env.HOME||Qv.tmpdir(),".bostrat"),oT=ts.join(rT,"repos"),vm=/^[^/\s]+\/[^/\s]+$/,sT=5*6e4,aT=6e4,iT=10*6e4,Tm=fetch,gl=new Map,Qn=null;Am=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),Br=(t,e)=>Object.assign(new Error(t),{status:e});cT=()=>tT(new URL("../bin/bostrat-git-credential",import.meta.url)),lT=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`});function _m(t=process.env){return!!($t(t)&&t.BOSTRAT_PLANE_TOKEN)}async function Rm(t,e,n){let r=$t(n),o=new AbortController,s=setTimeout(()=>o.abort(),fT);try{return await pT(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function $m(t,{env:e=process.env,force:n=!1}={}){if(!_m(e))return{facts:[],source:"unconfigured"};let r=xl.get(t);if(!n&&r&&Date.now()-r.at<dT)return{facts:r.facts,source:"cache"};try{let o=await Rm(`/v1/memory/${encodeURIComponent(t)}`,{headers:Im(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return xl.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 Ha(t,e,{env:n=process.env}={}){if(!_m(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await Rm(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Im(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 xl.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 dT,fT,xl,pT,Im,Om=C(()=>{oe();dT=6e4,fT=5e3,xl=new Map,pT=(...t)=>fetch(...t);Im=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`})});import U from"node:fs/promises";import mT from"node:os";import Z from"node:path";import{execFile as hT}from"node:child_process";import{promisify as wT}from"node:util";function Wa(){return Z.join(Sl,"memory","repos")}function tr(t){return t===Ue?Z.join(Sl,"memory",Ue):Z.join(Wa(),t)}async function ns(t){try{return JSON.parse(await U.readFile(Z.join(t,Dm),"utf8"))}catch{return{}}}async function vl(t,e){let n=await ns(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await U.mkdir(t,{recursive:!0}),await U.writeFile(Z.join(t,Dm),JSON.stringify(n))}function Mm(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 Pm(t){try{let{stdout:e}=await gT("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function Tl(t){if(!t)return[];let e=Mm(await Pm(t));if(e)return[e];let n=[],r=[];try{r=await U.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Mm(await Pm(Z.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function qa(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 hn(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function Ae(t,e=null){let n=e??await U.readFile(t,"utf8").catch(()=>""),{fm:r}=qa(n),o=hn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await U.stat(t)).mtimeMs}catch{return 0}}function Bm(t,e){let n=hn(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Ue){let o=hn(t,"trust");if(!o||/^operator/i.test(o))return Ue}return e}function Al(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=jr(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=jr(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 $o(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 $o(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 Ro(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,Oc(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Iv(){let t=X.join(Ic,"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 $c(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 Rv=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,$v=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 Ov(t,e){let n=Un(t),r=await Ro(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!Rv.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 Rc(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=Oc(l,[...s.values()].join(`
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 Mc(t,e=null){let n=Un(t),r=e?.length?e:await $o(n);if(!r.length)return{pushed:0,source:"empty"};let o=$v(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 Mv(t){let{facts:e,source:n}=await qf(t);e.length?await Ov(t,e):n==="plane"&&await Mc(t)}function Cv(t){let e=t.split(`
24
- `);e.length>Kf&&(e=e.slice(0,Kf));let n=e.join(`
25
- `);for(;Buffer.byteLength(n,"utf8")>Ev&&e.length;)e.pop(),n=e.join(`
26
- `);return n}var Nv=`# Track memory
27
-
28
- Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
29
- index and the fact files beside it. Facts you save here stay Claude-local but ride the
30
- same sync; prefer adding durable cross-provider facts to \`../\` following its format.
31
- `;async function Pv(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 Ro(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 $o(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}
19
+ `)}function jm(t,e){let n=hn(e,"name")||t.replace(/\.md$/,""),r=hn(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function rs(t){let e=[];try{e=await U.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 Lm(t,e){let n=Z.join(t,".bostrat","memory"),r=await rs(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=Z.join(n,l),d;try{d=await U.readFile(u,"utf8")}catch{continue}let{fm:f}=qa(d),p=Bm(f,e);if(!p)continue;let m=tr(p),w=Z.join(m,l),b=await Ae(u,d);if(i.has(m)||i.set(m,await ns(m)),(i.get(m)[l]||0)>=b)continue;let k=await Ae(w).catch(()=>0);if(await U.access(w).then(()=>!0).catch(()=>!1)&&k>=b)continue;await U.mkdir(m,{recursive:!0}),await U.writeFile(w,d),a++;let $=o.get(m);$||o.set(m,$={won:new Set,generated:new Map}),$.won.add(l),$.generated.set(l,jm(l,f)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await U.readFile(Z.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let d=Z.join(l,"MEMORY.md"),f=await U.readFile(d,"utf8").catch(()=>"");await U.writeFile(d,Al(f,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function bT(){let t=Z.join(Sl,"workspaces"),e=[];try{e=await U.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=Z.join(t,o.name);if(!await U.access(Z.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await Tl(s),c=await Lm(s,i||null);n+=c.harvested;for(let[l,u]of c.changedByKey){r.has(l)||r.set(l,new Set);for(let d of u)r.get(l).add(d)}}return{harvested:n,changedByKey:r}}async function ST(t,e){let n=tr(t),r=await ns(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!kT.test(l)||l==="MEMORY.md")continue;let u=Z.join(n,l),d=await U.access(u).then(()=>!0).catch(()=>!1),f=d?await Ae(u):0,p=Number(c.modified)||0;if(c.deleted){p>f&&(d&&await U.rm(u,{force:!0}),await vl(n,[[l,p]]),d&&(a.push(l),i++));continue}d&&f>=p||(r[l]||0)>=p||(await U.mkdir(n,{recursive:!0}),await U.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=Z.join(n,"MEMORY.md"),l=await U.readFile(c,"utf8").catch(()=>""),u=Al(l,[...s.values()].join(`
20
+ `),o);if(a.length){let d=new Set(a);u=u.split(`
21
+ `).filter(f=>!d.has(jr(f))).join(`
22
+ `)}await U.mkdir(n,{recursive:!0}),await U.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function El(t,e=null){let n=tr(t),r=e?.length?e:await rs(n);if(!r.length)return{pushed:0,source:"empty"};let o=xT(await U.readFile(Z.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=Z.join(n,a),c;try{c=await U.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await Ae(i,c),indexLine:o.get(a)??null})}return Ha(t,s)}async function vT(t){let{facts:e,source:n}=await $m(t);e.length?await ST(t,e):n==="plane"&&await El(t)}function TT(t){let e=t.split(`
23
+ `);e.length>Cm&&(e=e.slice(0,Cm));let n=e.join(`
24
+ `);for(;Buffer.byteLength(n,"utf8")>yT&&e.length;)e.pop(),n=e.join(`
25
+ `);return n}async function ET(t,e){if(!t||!e?.length)return{materialized:!1,reason:"no-repo-key"};let n=Z.join(t,".bostrat","memory"),r=0,o=0,s=[];for(let u of e){let d=tr(u),f=await ns(d);for(let[b,k]of Object.entries(f)){let S=Z.join(n,b);await U.access(S).then(()=>!0).catch(()=>!1)&&await Ae(S)<=k&&(await U.rm(S,{force:!0}),o++)}let p=await rs(d);if(!p.length)continue;await U.mkdir(n,{recursive:!0});for(let b of p){let k=Z.join(d,b),S=Z.join(n,b),$=await Ae(k),R=await Ae(S).catch(()=>0);await U.access(S).then(()=>!0).catch(()=>!1)&&R>=$||(await U.copyFile(k,S),r++)}let m=await U.readFile(Z.join(d,"MEMORY.md"),"utf8").catch(()=>""),w=u===Ue?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${w}
32
26
 
33
27
  ${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
28
 
35
29
  `)+`
36
- `,c=Cv(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"),Nv,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var zf=0,Dv=3e4;async function Qf(t){try{let e=await $c(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Me];if(Date.now()-zf>Dv){zf=Date.now(),await Bv();let{changedByKey:r}=await Iv();for(let[o,s]of r)await Mc(o,[...s])}for(let r of n)await Mv(r);return await Pv(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function Bv(){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 $o(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 Rc(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,Oc(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 $o(r),s=await Ro(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 Rc(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 $c(t),{harvested:n,changedByKey:r}=await Zf(t,e[0]||null);for(let[o,s]of r)await Mc(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import Bc 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 Cc from"node:fs/promises";import jv from"node:path";var Lv=0;async function Ft(t,e,{mode:n}={}){await Cc.mkdir(jv.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++Lv}.tmp`;await Cc.writeFile(r,JSON.stringify(e,null,2)+`
41
- `,n!=null?{mode:n}:void 0),await Cc.rename(r,t)}var Fv=1e4,Uv=3e4,rp=2e3,Hv=5,op=100,Wv=60*6e4,qv=()=>process.env.BOSTRAT_STATE_DIR||lp.join(process.env.HOME||cp.tmpdir(),".bostrat"),up=()=>lp.join(qv(),"content-outbox.json"),Kv=()=>process.env.BOSTRAT_NODE_ID||cp.hostname(),Gv=(...t)=>fetch(...t),Vv=Uv,zv=Wv;function ht(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Mo=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Co(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),Fv);try{return await Gv(`${Qt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var la=null,Nc=null,ca=null,Oo=null;async function dp(){return la||(Nc||=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(()=>Nc=null),Nc)}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 Dc(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(()=>{})},Vv),ca.unref?.())}function Pc(t){let e=new Error(`plane ${t}`);return e.refused=!0,e}async function Jv(t,e=process.env){if(t.op==="record"){let n=await Co(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...Mo(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:Kv()}]})},e);if(!n.ok)throw Pc(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 Co(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...Mo(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw Pc(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 Co(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:Mo(e)},e);if(!n.ok)throw Pc(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)?Oo||(Oo=(async()=>{let r=await dp();Xv(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await Jv(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<Hv)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(()=>{Oo=null}),Oo):{flushed:0,source:"unconfigured"}}function Xv(t,e){if(!t.parked.length||t.ops.length)return;let n=t.parked.filter(r=>e-(r.parkedAt||0)>=zv);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 Dc({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 Dc({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 Dc({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 Co(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:Mo(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 Co(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Mo(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 Yv=".bostrat/handoff",Zv="latest.md",Qv=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=Qv(t);if(!e||!ht())return{restored:!1};let n=fp.join(t,Yv,Zv);if(await Bc.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 Bc.mkdir(fp.dirname(n),{recursive:!0}),await Bc.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import No from"node:fs/promises";import tS from"node:os";import wp from"node:path";var eS=[/[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 eS)e=e.replace(n,"\u2039redacted\u203A");return e}var jc=process.env.BOSTRAT_DEBUG_DIR||wp.join(tS.homedir(),".bostrat","debug"),Sr=wp.join(jc,"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 nS(t){try{await No.mkdir(jc,{recursive:!0}),await No.appendFile(Sr,`${JSON.stringify(t)}
42
- `,"utf8");let n=(await No.readFile(Sr,"utf8")).split(`
43
- `).filter(Boolean);n.length>hp&&await No.writeFile(Sr,`${n.slice(-hp).join(`
30
+ `,c=TT(i);await U.mkdir(n,{recursive:!0}),await U.writeFile(Z.join(n,"MEMORY.md"),c);let l=Z.join(n,"claude");return await U.mkdir(l,{recursive:!0}),await U.writeFile(Z.join(l,"MEMORY.md"),AT,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Fm(t){try{let e=await Tl(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Ue];if(Date.now()-Nm>_T){Nm=Date.now(),await IT();let{changedByKey:r}=await bT();for(let[o,s]of r)await El(o,[...s])}for(let r of n)await vT(r);return await ET(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function IT(){let t=0,e=[];try{e=await U.readdir(Wa(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=Z.join(Wa(),n.name),o=tr(Ue),s=new Set,a=new Map;for(let p of await rs(r)){let m=Z.join(r,p),w;try{w=await U.readFile(m,"utf8")}catch{continue}let{fm:b}=qa(w);if(Bm(b,n.name)!==Ue)continue;let k=Z.join(o,p),S=await Ae(k).catch(()=>0),$=await Ae(m,w);await U.mkdir(o,{recursive:!0}),$>S&&await U.writeFile(k,w),await U.rm(m,{force:!0}),s.add(p),a.set(p,jm(p,b)),t++}if(!s.size)continue;let i=Date.now();await vl(r,[...s].map(p=>[p,i])),await Ha(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=Z.join(r,"MEMORY.md"),l=await U.readFile(c,"utf8").catch(()=>""),u=l.split(`
31
+ `).filter(p=>!s.has(jr(p))).join(`
32
+ `);await U.writeFile(c,u).catch(()=>{});let d=Z.join(o,"MEMORY.md"),f=await U.readFile(d,"utf8").catch(()=>"");await U.writeFile(d,Al(f,l,s,a))}return{moved:t}}async function Um(){let t=[],e=[Ue];try{for(let n of await U.readdir(Wa(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=tr(n),o=await rs(r),s=await ns(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await U.readFile(Z.join(r,i),"utf8").catch(()=>""),{fm:l}=qa(c);a.push({file:i,name:hn(l,"name")||i.replace(/\.md$/,""),description:hn(l,"description")||"",scope:hn(l,"scope")||null,modified:await Ae(Z.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function Hm(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=tr(t),r=Z.join(n,e);if(!await U.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await U.rm(r,{force:!0}),await vl(n,[[e,s]]);let a=Z.join(n,"MEMORY.md"),i=await U.readFile(a,"utf8").catch(()=>"");await U.writeFile(a,i.split(`
33
+ `).filter(l=>jr(l)!==e).join(`
34
+ `)).catch(()=>{});let c=await Ha(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function Wm(t){try{let e=await Tl(t),{harvested:n,changedByKey:r}=await Lm(t,e[0]||null);for(let[o,s]of r)await El(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}var gT,Sl,Cm,yT,Ue,Dm,jr,kT,xT,AT,Nm,_T,Ka=C(()=>{Om();gT=wT(hT),Sl=process.env.BOSTRAT_STATE_DIR||Z.join(process.env.HOME||mT.tmpdir(),".bostrat"),Cm=200,yT=25*1024;Ue="operator";Dm=".tombstones.json";jr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;kT=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,xT=t=>{let e=new Map;for(let n of(t||"").split(`
35
+ `)){let r=jr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};AT=`# Track memory
36
+
37
+ Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
38
+ index and the fact files beside it. Facts you save here stay Claude-local but ride the
39
+ same sync; prefer adding durable cross-provider facts to \`../\` following its format.
40
+ `;Nm=0,_T=3e4});import _l from"node:fs/promises";import RT from"node:path";async function zt(t,e,{mode:n}={}){await _l.mkdir(RT.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++$T}.tmp`;await _l.writeFile(r,JSON.stringify(e,null,2)+`
41
+ `,n!=null?{mode:n}:void 0),await _l.rename(r,t)}var $T,wn=C(()=>{$T=0});import zm from"node:fs/promises";import Jm from"node:os";import Ym from"node:path";function gt(t=process.env){return!!($t(t)&&t.BOSTRAT_PLANE_TOKEN)}async function as(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),OT);try{return await BT(`${$t(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}async function Zm(){return Va||(Il||=zm.readFile(Xm(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>Va={ops:Array.isArray(t?.ops)?t.ops:[],parked:Array.isArray(t?.parked)?t.parked:[]}).finally(()=>Il=null),Il)}async function za(){await zt(Xm(),Va).catch(()=>{})}function Gm(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function $l(t){let e=await Zm(),n=Gm(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=>Gm(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>qm&&(e.ops=e.ops.slice(-qm)),await za()}function Vm(){Ga||(Ga=setTimeout(()=>{Ga=null,Lr().catch(()=>{})},jT),Ga.unref?.())}function Rl(t){let e=new Error(`plane ${t}`);return e.refused=!0,e}async function FT(t,e=process.env){if(t.op==="record"){let n=await as(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...ss(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:DT()}]})},e);if(!n.ok)throw Rl(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 zm.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await as(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...ss(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw Rl(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 as(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:ss(e)},e);if(!n.ok)throw Rl(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function Lr({env:t=process.env,log:e=console,now:n=Date.now()}={}){return gt(t)?os||(os=(async()=>{let r=await Zm();UT(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await FT(a,t)}catch(c){if(!c?.refused)return Vm(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<MT)return Vm(),await za(),{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>Km&&(r.parked=r.parked.slice(-Km)),s++,await za();continue}i.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await za()}return{flushed:o,parked:s,pending:0}})().finally(()=>{os=null}),os):{flushed:0,source:"unconfigured"}}function UT(t,e){if(!t.parked.length||t.ops.length)return;let n=t.parked.filter(r=>e-(r.parkedAt||0)>=LT);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 Ft(t,e,{trackId:n=null,env:r=process.env}={}){return gt(r)?(await $l({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),Lr({env:r})):{queued:!1,source:"unconfigured"}}async function gn(t,e,n,r,o,{env:s=process.env}={}){return gt(s)?(await $l({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),Lr({env:s})):{queued:!1,source:"unconfigured"}}async function pe(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return gt(r)?(await $l({op:"delete",family:t,id:e,deletedAt:n}),Lr({env:r})):{queued:!1,source:"unconfigured"}}async function Ot(t,{env:e=process.env}={}){if(!gt(e))return{records:[],source:"unconfigured"};try{let n=await as(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:ss(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 is(t,e,n,r,{env:o=process.env}={}){if(!gt(o))return null;try{let s=await as(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:ss(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 OT,CT,qm,MT,Km,PT,NT,Xm,DT,BT,jT,LT,ss,Va,Il,Ga,os,yn=C(()=>{oe();wn();OT=1e4,CT=3e4,qm=2e3,MT=5,Km=100,PT=60*6e4,NT=()=>process.env.BOSTRAT_STATE_DIR||Ym.join(process.env.HOME||Jm.tmpdir(),".bostrat"),Xm=()=>Ym.join(NT(),"content-outbox.json"),DT=()=>process.env.BOSTRAT_NODE_ID||Jm.hostname(),BT=(...t)=>fetch(...t),jT=CT,LT=PT;ss=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});Va=null,Il=null,Ga=null,os=null});import Ol from"node:fs/promises";import Qm from"node:path";async function th(t,e,{box:n=null,agent:r=null}={}){if(!t||!gt())return{pushed:!1};try{let o=Date.now();return await Ft("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await gn("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function eh(t){try{let e=qT(t);if(!e||!gt())return{restored:!1};let n=Qm.join(t,HT,WT);if(await Ol.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await Ot("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 is("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Ol.mkdir(Qm.dirname(n),{recursive:!0}),await Ol.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var HT,WT,qT,Cl=C(()=>{yn();HT=".bostrat/handoff",WT="latest.md",qT=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null});import cs from"node:fs/promises";import KT from"node:os";import rh from"node:path";function er(t){let e=String(t??"");for(let n of GT)e=e.replace(n,"\u2039redacted\u203A");return e}function Ja(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function VT(t){try{await cs.mkdir(Ml,{recursive:!0}),await cs.appendFile(Fr,`${JSON.stringify(t)}
42
+ `,"utf8");let n=(await cs.readFile(Fr,"utf8")).split(`
43
+ `).filter(Boolean);n.length>nh&&await cs.writeFile(Fr,`${n.slice(-nh).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 nS(e)}async function gp({limit:t=80}={}){try{return(await No.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:jc,LOG_FILE:Sr};var ot=oS(rS);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 aS({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=aS({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return ma.existsSync(kp(bp))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:bp}),iS=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Fc=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",cS="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";function Uc(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${cS}=1`]}var Hc="bostrat-usage-probe",lS=new Set([Hc]);function Wc(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 uS(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",Lc=5e3,dS=[250,1e3,3e3],fS=t=>new Promise(e=>setTimeout(e,t));async function vp(t,{loginShell:e=!1,timeoutMs:n=Lc}={}){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=dS,sleep:n=fS,probe:r=vp}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Lc});for(let a=0;s===Ar&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:Lc*2});return s}var qc={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 Po(){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 pS=/^[A-Za-z0-9_.-]{1,64}$/;function wt(t){return typeof t=="string"&&pS.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=>!lS.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function tt(t){return(await Ce()).find(n=>n.name===t)||null}var mS=/^[A-Za-z0-9 ._↻-]+$/,hS=64;function wS(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=hS&&mS.test(e)}function Kc(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 gS=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,yS=1e3,bS=/not found|Unknown command/i;function kS(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 xS(t,e){let n=Date.now()+gS(),r=kS(e),o=!1,s=0;for(;;){if(await _r(yS),s+=1,!await Sp(t))return;let a=await mt(t,{lines:15}),i=!o&&bS.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=iS,n={}){if(!wS(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Kc(r);if(!wt(o))throw new Error("resulting tmux session name is invalid");if(!await Po())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||Fc,a=n.agent||ln(s),i=!!n.resumeSessionId,c=n.resumeSessionId||sS(),l=i?s:Wc(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(...Uc());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")),uS(l)&&xS(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 vS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,SS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,_t=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,TS=Number(process.env.BOSTRAT_BOX_COLS)||120,AS=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=TS,rows:n=AS}={}){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 _S(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 _S(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=vS,rows:n=SS}={}){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 ES=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=>ES.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 IS 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=IS.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&&Gc(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Gc(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"&&MS(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{}OS(e)})}var RS=6,$S=250;async function OS(t,e=RS,n=$S){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 MS(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 Gc(t,e){try{t.send(JSON.stringify(e))}catch{}}function ka(t,e){Gc(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 Do from"node:fs";import CS from"node:fs/promises";import Ir from"node:path";import NS 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},PS=2e5,Vc=process.env.BOSTRAT_CLAUDE_PROJECTS||Ir.join(NS.homedir(),".claude","projects");function zc(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Jc(t){let e=Ir.join(Vc,zc(t)),n;try{n=Do.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}=Do.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(Vc,zc(t),`${e}.jsonl`);return Do.existsSync(n)?n:null}function Cp(t){if(!t)return[];let e=Ir.join(Vc,zc(t)),n;try{n=Do.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Do.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 CS.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 DS(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 Bo(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=Jc(t),o=!1,!r)return null}else return null;let s;try{s=DS(await Ut(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Mp,s.model),i=Mp[s.model]||PS;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 BS(t,e){return!t||!e?null:Ne(t,e)}function jS(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 LS(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 FS(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Xc(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=Xc(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 US=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Yc(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"&&US.test(h)&&s.set(m.id,h.split("/").pop())}if(FS(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=jS(l.content),d=u==="assistant"?LS(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 HS=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function Pp(t,e,{turns:n=16}={}){let r=BS(t,e);if(!r)return"";let o=await Yc(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}:** ${HS(s.text||c,700)} ${i}`.trim()}).join(`
53
-
54
- `):""}import JA from"node:fs";import hn from"node:fs/promises";import wn from"node:path";import WS from"node:fs/promises";import{execFile as qS}from"node:child_process";import{promisify as KS}from"node:util";var Zc=KS(qS),GS=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Dp=new Set,Bp=Promise.resolve(),Qc=null;function Lp(t){Qc=typeof t=="function"?t:null}function VS(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(()=>zS({sessionName:t,from:e,to:r})).catch(()=>{}))}async function zS({sessionName:t,from:e,to:n}){await q({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Zc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await WS.realpath(r),s=VS(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Zc("npm",["install","-g","@openai/codex"],{env:a,timeout:GS});let i=(await Zc(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(Qc)try{await Qc(t)}catch{}}var JS=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Up=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function XS(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(JS);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 YS(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 ZS(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 QS(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])&&YS(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=ZS(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Wp=/Enter this one-time code/,tT=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function eT(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(tT);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 nT(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 rT(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 tl=/Run Everything|Add a follow-up|Plan, search, build anything/;function oT(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||tl.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 sT=/click this link to log in|Signing in with the browser/;function aT(t,e=""){let n=String(t);if(!sT.test(n)||tl.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 iT(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||tl.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=eT(e,n)||nT(e,n)||rT(e,n)||QS(e,n);if(r)return r}if(t==="cursor"){let r=aT(e,n)||iT(e,n)||oT(e,n);if(r)return r}return XS(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 cT=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,el=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,jo=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/}},lT=/Trust this workspace|\[a\]\s*Trust/,uT=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function dT(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(uT);return{from:r?.[1]||null,to:r?.[2]||null}}function fn(t){return new Promise(e=>setTimeout(e,t))}var fT=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,va=new Map;function Vp(t,e){let n=Date.now(),r=fT();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 pT=68,mT=/^\s*[─━]{10,}\s*$/;function hT(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 nl(t,e){let n=Ot[t];if(!n?.busy)return{state:"idle",reason:null};if(ol(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=hT(e);return t==="claude"&&r>0&&r<pT?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function rl(t,e){return nl(t,e).state}var wT=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,gT=15;function Jp(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
- `).slice(-gT).join(`
64
- `);return wT.test(n)}function Lo(t,e){return rl(t,e)==="busy"}function Sa(t,e){return rl(t,e)!=="idle"}function Ta(t,e){let n=Ot[t];return n?.up?n.up.test(e):!0}function yT(t,e){let n=Ot[t];return n?ol(t,e)?!1:n.up.test(e)&&rl(t,e)==="idle":!0}function ol(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(yT(e,s)){await fn(jo);return}if(e==="cursor"&&lT.test(s)&&!Ot.cursor.up.test(s)&&o<3){if(!Vp(t,"cursor-trust")){await fn(el);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(jo);continue}if(e==="codex"&&o<3){let a=dT(s);if(a){if(!Vp(t,`codex-update:${a.from}->${a.to}`)){await fn(el);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(jo);continue}}if(Date.now()>r){if(ol(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(jo);return}await fn(el)}}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=cT()}={}){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(jo)}var bT=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),kT=(()=>{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:bT}:t==="claude"?{enter:!0,submitDelayMs:kT}:{enter:!0}}import Ea from"node:fs/promises";import nm from"node:path";var xT=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],vT={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 xT){let o=0;for(let s of vT[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var ST=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),TT=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Yp(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))ST.has(r)&&(e+=o||0),TT.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Fo(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,AT=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=Fo(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||AT.has(r)||n.push(r);return n}function _T(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 Uo(t){let e=_T(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 ET(t){let e=nm.join(t,".bostrat","events"),n;try{n=await Ea.readdir(e)}catch{return Uo([])}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 Uo(r)}function IT(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 RT(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 ET(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=RT(i,IT(a),{k:r,maxChars:o,exclude:s});return c?(await Ea.appendFile(e,`
45
+ `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${Fr}: ${e?.message||e}`)}}async function G(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&&Ja())try{e.paneTail=er((await bt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(Ja()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await VT(e)}async function oh({limit:t=80}={}){try{return(await cs.readFile(Fr,"utf8")).split(`
46
+ `).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=er(o.paneTail)),o}catch{return{raw:er(r)}}})}catch{return[]}}var GT,Ml,Fr,nh,sh,He=C(()=>{Ut();GT=[/[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];Ml=process.env.BOSTRAT_DEBUG_DIR||rh.join(KT.homedir(),".bostrat","debug"),Fr=rh.join(Ml,"handoff.jsonl"),nh=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;sh={DEBUG_DIR:Ml,LOG_FILE:Fr}});import{execFile as zT}from"node:child_process";import{promisify as JT}from"node:util";import Xa from"node:fs";import{randomUUID as YT}from"node:crypto";function Wr(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}function ih(t){return`/tmp/tmux-${t}/default`}function XT({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=ih(r);return e?o:n?`${n}/bostrat/tmux-default`:o}function Dl(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${QT}=1`]}function jl(t,e,n){let r=nr(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&&kt(n)&&(s+=` --name ${n}`),s}function nr(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function bn(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function eA(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:nr(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function ch(t,{loginShell:e=!1,failOpen:n=!1}={}){let r=await lh(t,{loginShell:e});return r===Za?!0:r===Ur?!1:n}async function lh(t,{loginShell:e=!1,timeoutMs:n=Pl}={}){if(!t)return Ur;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}),Za}catch(s){return s?.killed||s?.signal?Hr:s?.code===1||s?.code===127?Ur:Hr}}async function Qa(t,{retries:e=nA,sleep:n=rA,probe:r=lh}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Pl});for(let a=0;s===Hr&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:Pl*2});return s}function B(...t){return["-S",Qt,...t]}async function kn(){if(!Xa.existsSync(Qt))return!1;try{return await it(N,B("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function ls(){if(await kn())return!0;try{Xa.mkdirSync(Qt.replace(/\/[^/]+$/,""),{recursive:!0}),await it(N,B("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await kn()}function kt(t){return typeof t=="string"&&oA.test(t)}async function me(){if(!Xa.existsSync(Qt))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(N,B("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
47
+ `).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,f,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:d||null,agent:f||(l==="1"?"claude":null),alternateOn:p==="1"}}).filter(r=>!tA.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function tt(t){return(await me()).find(n=>n.name===t)||null}function iA(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=aA&&sA.test(e)}function Fl(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function uh(t){try{let{stdout:e}=await it(N,B("list-sessions","-F","#{session_name}"));return e.split(`
48
+ `).map(n=>n.trim()).includes(t)}catch{return!1}}function dA(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 fA(t,e){let n=Date.now()+cA(),r=dA(e),o=!1,s=0;for(;;){if(await Wr(lA),s+=1,!await uh(t))return;let a=await bt(t,{lines:15}),i=!o&&uA.test(a)&&!!r?.test(a);if(!i&&s>1&&!await qr(t))return;if(i){o=!0,await G({flow:"spawn",phase:"launch-retry",sessionName:t,agent:bn(e),promptHead:e.slice(0,120)}).catch(()=>{}),await it(N,B("send-keys","-t",t,"C-u")),await it(N,B("send-keys","-t",t,"-l",e)),await it(N,B("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await G({flow:"spawn",phase:"launch-failed",sessionName:t,agent:bn(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function rr(t,e=ZT,n={}){if(!iA(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Fl(r);if(!kt(o))throw new Error("resulting tmux session name is invalid");if(!await ls())throw new Error(`host tmux server not running on ${Qt} and auto-start failed \u2014 start one: ${N} -S ${Qt} new-session -d -s keeper "sleep 2147483647"`);if(await uh(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Nl,a=n.agent||bn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||YT(),l=i?s:jl(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&nr(s),f=n.firstRun===!0;f||(d&&await pn(e),a==="codex"&&await Nr(e),a==="cursor"&&await Dr(e),await Fm(e),await eh(e));let p=d&&!f?Da():null,m=mm(),w=["-e",`${Pa}=${m}`];p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),d&&w.push(...Dl());try{await it(N,B("new-session","-d","-s",o,"-c",e,...w))}catch(b){throw Ya(p?Ya(b,p):b,m)}return await or(o),await it(N,B("set-option","-t",o,"@bostrat-session","1")),await it(N,B("set-option","-t",o,"@bostrat-title",r)),await it(N,B("set-option","-t",o,"@bostrat-sid",c)),await it(N,B("set-option","-t",o,"@bostrat-agent",a)),await it(N,B("send-keys","-t",o,l,"Enter")),eA(l)&&fA(o,l).catch(()=>{}),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function Ee(t){if(!kt(t))throw new Error("invalid session name");await it(N,B("kill-session","-t",t))}async function dh(t,e){if(kt(t))try{let n=e?B("set-option","-t",t,"@bostrat-sid",e):B("set-option","-u","-t",t,"@bostrat-sid");await it(N,n)}catch{}}async function bt(t,{lines:e=40}={}){if(!kt(t))return"";try{let n=B("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await it(N,n);return String(r||"")}catch{return""}}async function fh(t){if(!kt(t))return 0;try{let{stdout:e}=await it(N,B("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
49
+ `).filter(Boolean).length}catch{return 0}}async function or(t,{cols:e=hA,rows:n=wA}={}){if(!kt(t))return!0;if(await fh(t))return!1;try{let r=await ph(t);if(r.cols===e&&r.rows===n)return!0;await it(N,B("resize-window","-t",t,"-x",String(e),"-y",String(n))),await it(N,B("set-window-option","-t",t,"window-size","latest")),await Wr(80)}catch{}return!0}async function gA(t){if(!kt(t))return 0;try{let{stdout:e}=await it(N,B("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function qr(t){let e=await gA(t);if(!e)return!1;try{return await it("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function ph(t){if(!kt(t))return{cols:0,rows:0};try{let{stdout:e}=await it(N,B("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 ti(t,{cols:e=pA,rows:n=mA}={}){if(kt(t)&&!await fh(t))try{let r=await ph(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await it(N,B("resize-window","-t",t,"-x",String(o),"-y",String(s))),await it(N,B("set-window-option","-t",t,"window-size","latest")),await Wr(80))}catch{}}async function xn(t){return await ti(t),bt(t,{lines:Ct})}async function vt(t,e,{settleMs:n=40,kind:r}={}){if(!kt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>yA.has(s));for(let s=0;s<o.length;s++)await it(N,B("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await Wr(n);return G({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Kr(t,e,{kind:n}={}){if(!kt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await it(N,B("send-keys","-t",t,"-N",String(r),"BSpace")),G({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function ni(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!kt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await it(N,B("send-keys","-t",t,"-l",s)),n>0&&await Wr(n);G({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function Mt(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!kt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await it(N,B("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await Wr(s),await it(N,B("send-keys","-t",t,"Enter"))}G({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(()=>{})}var it,N,ah,Qt,ZT,Nl,QT,Bl,tA,Za,Ur,Hr,Pl,nA,rA,Ll,oA,sA,aA,cA,lA,uA,pA,mA,Ct,hA,wA,yA,ei,Ut=C(()=>{Xo();ja();La();Ka();Cl();He();Yo();it=JT(zT);N=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";ah=typeof process.getuid=="function"?process.getuid():0,Qt=XT({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return Xa.existsSync(ih(ah))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:ah}),ZT=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Nl=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",QT="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";Bl="bostrat-usage-probe",tA=new Set([Bl]);Za="present",Ur="absent",Hr="unknown",Pl=5e3,nA=[250,1e3,3e3],rA=t=>new Promise(e=>setTimeout(e,t));Ll={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"};oA=/^[A-Za-z0-9_.-]{1,64}$/;sA=/^[A-Za-z0-9 ._↻-]+$/,aA=64;cA=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,lA=1e3,uA=/not found|Unknown command/i;pA=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,mA=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Ct=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,hA=Number(process.env.BOSTRAT_BOX_COLS)||120,wA=Number(process.env.BOSTRAT_BOX_ROWS)||40;yA=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);ei=1600});import us from"node:fs";import TA from"node:fs/promises";import Gr from"node:path";import AA from"node:os";function Wl(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function ql(t){let e=Gr.join(Hl,Wl(t)),n;try{n=us.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Gr.join(e,o);try{let{mtimeMs:a}=us.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function We(t,e){if(!e)return null;let n=Gr.join(Hl,Wl(t),`${e}.jsonl`);return us.existsSync(n)?n:null}function kh(t){if(!t)return[];let e=Gr.join(Hl,Wl(t)),n;try{n=us.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=us.statSync(Gr.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Jt(t,e=131072){let n=await TA.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 _A(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 ds(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=We(t,e),o=!0,!r)return null}else if(n){if(r=ql(t),o=!1,!r)return null}else return null;let s;try{s=_A(await Jt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(bh,s.model),i=bh[s.model]||EA;return{source:"transcript",exact:o,model:s.model,windowKnown:a,window:i,occupancy:s.occupancy,pct:Math.min(1,s.occupancy/i),transcript:r}}var bh,EA,Hl,he=C(()=>{bh={"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},EA=2e5,Hl=process.env.BOSTRAT_CLAUDE_PROJECTS||Gr.join(AA.homedir(),".claude","projects")});function IA(t,e){return!t||!e?null:We(t,e)}function RA(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 $A(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 OA(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Kl(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 xh(t,e){if(!t.uuid)return null;let n=Kl(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}async function Gl(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Jt(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 w=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof w=="string"&&CA.test(w)&&s.set(m.id,w.split("/").pop())}if(OA(l.content)){let m=xh(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=RA(l.content),f=u==="assistant"?$A(l.content):null,p=u==="user"?xh(c,s):null;!d&&!f&&!p||o.push({role:u,text:d,tools:f||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}async function Sh(t,e,{turns:n=16}={}){let r=IA(t,e);if(!r)return"";let o=await Gl(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}:** ${MA(s.text||c,700)} ${i}`.trim()}).join(`
53
+
54
+ `):""}var CA,MA,oi=C(()=>{he();CA=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;MA=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t});import PA from"node:fs/promises";import{execFile as NA}from"node:child_process";import{promisify as DA}from"node:util";function Eh(t){zl=typeof t=="function"?t:null}function jA(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function _h({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||vh.has(r)||(vh.add(r),Th=Th.then(()=>LA({sessionName:t,from:e,to:r})).catch(()=>{}))}async function LA({sessionName:t,from:e,to:n}){await G({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Vl("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await PA.realpath(r),s=jA(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Vl("npm",["install","-g","@openai/codex"],{env:a,timeout:BA});let i=(await Vl(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await G({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await Ah({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await G({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await Ah({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function Ah(t){if(zl)try{await zl(t)}catch{}}var Vl,BA,vh,Th,zl,Jl=C(()=>{He();Vl=DA(NA),BA=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,vh=new Set,Th=Promise.resolve(),zl=null});function UA(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
55
+ `);if(!Ih.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(FA);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"&&Ih.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:""}))}]}}function Vr(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
56
+ `);for(let r=n.length-1;r>=0;r--)if(Oh.test(n[r]))return n.slice(r).join(`
57
+ `);return e}function HA(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 WA(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":""}function qA(t,e=""){let n=Vr(t);if(si.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(Rh);if(!u)continue;s<0&&(s=l);let d="";for(let f=l+1;f<Math.min(l+3,r.length);f++){let p=r[f].trim();if(p){if(Rh.test(r[f])||/^Press enter/.test(p))break;d=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!Oh.test(r[l])&&HA(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=WA(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function GA(t,e=""){let n=Vr(t);if(si.test(n)||!$h.test(n))return null;let r=n.split(`
59
+ `),o=r.findIndex(i=>$h.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(KA);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 VA(t,e=""){let n=Vr(t);return si.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 zA(t,e=""){let n=Vr(t);return si.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:""}]}]}}function JA(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||Yl.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"}]}]}}function XA(t,e=""){let n=String(t);if(!YA.test(n)||Yl.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 ZA(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||Yl.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 Ch(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=GA(e,n)||VA(e,n)||zA(e,n)||qA(e,n);if(r)return r}if(t==="cursor"){let r=XA(e,n)||ZA(e,n)||JA(e,n);if(r)return r}return UA(e,{provider:t,box:n})}function Mh(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 FA,Ih,si,Oh,Rh,$h,KA,Yl,YA,ai=C(()=>{FA=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Ih=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;si=/(^|\n)\s*›/,Oh=/^\s*Welcome to Codex\b/;Rh=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;$h=/Enter this one-time code/,KA=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Yl=/Run Everything|Add a follow-up|Plan, search, build anything/;YA=/click this link to log in|Signing in with the browser/});function nE(t){let e=String(t||"");if(!Dt.codex.block.test(e))return null;let n=Vr(e);if(!Dt.codex.block.test(n))return null;let r=n.match(eE);return{from:r?.[1]||null,to:r?.[2]||null}}function Sn(t){return new Promise(e=>setTimeout(e,t))}function Ph(t,e){let n=Date.now(),r=rE();for(let[s,a]of ii)n-a>=r&&ii.delete(s);let o=`${t}::${e}`;return ii.has(o)?!1:(ii.set(o,n),!0)}function aE(t){let e=0;for(let n of String(t).split(`
62
+ `))sE.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function Zl(t,e){let n=Dt[t];if(!n?.busy)return{state:"idle",reason:null};if(tu(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=aE(e);return t==="claude"&&r>0&&r<oE?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Ql(t,e){return Zl(t,e).state}function Dh(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
+ `).slice(-cE).join(`
64
+ `);return iE.test(n)}function ps(t,e){return Ql(t,e)==="busy"}function ci(t,e){return Ql(t,e)!=="idle"}function li(t,e){let n=Dt[t];return n?.up?n.up.test(e):!0}function lE(t,e){let n=Dt[t];return n?tu(t,e)?!1:n.up.test(e)&&Ql(t,e)==="idle":!0}function tu(t,e){let n=Dt[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 Nh(t,e,n){let r=Date.now()+n,o=0;for(await or(t).catch(()=>{});;){let s=await bt(t);if(lE(e,s)){await Sn(fs);return}if(e==="cursor"&&tE.test(s)&&!Dt.cursor.up.test(s)&&o<3){if(!Ph(t,"cursor-trust")){await Sn(Xl);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await Mt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await Sn(fs);continue}if(e==="codex"&&o<3){let a=nE(s);if(a){if(!Ph(t,`codex-update:${a.from}->${a.to}`)){await Sn(Xl);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await Mt(t,"3",{enter:!1,kind:"codex-update-skip"}),_h({sessionName:t,from:a.from,to:a.to}),await Sn(fs);continue}}if(Date.now()>r){if(tu(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Dt.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"&&Dt.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"&&Dt.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"&&Dt.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'),G({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await qr(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 G({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.`),G({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Dt[e]?.block?.test(s),paneUp:!!Dt[e]?.up?.test(s),paneBusy:!!Dt[e]?.busy?.test(s)}).catch(()=>{}),await Sn(fs);return}await Sn(Xl)}}function Bh({agent:t,agentCmd:e}={}){let n=e?bn(e):bn(t||"");return{label:n,realClaude:e?nr(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 ui({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Bh({agent:n,agentCmd:r});return!e||!o&&!s?null:vn({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function vn({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=QA()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Bh({agent:r,agentCmd:o});if(i&&t&&await pn(t),c&&t&&await Nr(t),l&&t&&await Dr(t),(i||c||l)&&n){await Nh(n,a,s);return}let u=Dt[r]?r:Dt[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Nh(n,u,s);return}await Sn(fs)}var QA,Xl,fs,Dt,tE,eE,rE,ii,oE,sE,iE,cE,di=C(()=>{Ut();Xo();ja();La();Jl();ai();He();QA=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Xl=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,fs=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Dt={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/}},tE=/Trust this workspace|\[a\]\s*Trust/,eE=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;rE=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,ii=new Map;oE=68,sE=/^\s*[─━]{10,}\s*$/;iE=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,cE=15});function _e(t){return t==="codex"?{enter:!0,submitDelayMs:uE}:t==="claude"?{enter:!0,submitDelayMs:dE}:{enter:!0}}var uE,dE,ms=C(()=>{uE=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),dE=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})()});function sr(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of fE){let o=0;for(let s of pE[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function jh(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))mE.has(r)&&(e+=o||0),hE.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function hs(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 fE,pE,mE,hE,eu=C(()=>{fE=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],pE={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/]};mE=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),hE=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function Hh(t){let e=hs(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||wE.has(r)||n.push(r);return n}function gE(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(Uh(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(Uh(n,"assistant",o))}}return e}function Uh(t,e,n){let r=sr(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 ws(t){let e=gE(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=Hh(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,d]of l){let f=n.get(u);f||n.set(u,f=new Map),f.set(i,d)}});let s=e.length;return{chunks:e,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function fi(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(Hh(e))];if(!i.length)return[];let c=new Map;for(let u of i){let d=t.postings.get(u);if(!d)continue;let f=d.size,p=Math.log(1+(t.N-f+.5)/(f+.5));for(let[m,w]of d){let b=t.lengths[m],k=w+Lh*(1-Fh+Fh*(b/(t.avgdl||1))),S=p*(w*(Lh+1)/k);c.set(m,(c.get(m)||0)+S)}}let l=[];for(let[u,d]of c){let f=t.chunks[u];o&&f.role!==o||s&&!f.tags.includes(s)||a&&f.boxId!==a||l.push({chunk:f,score:d})}return l.sort((u,d)=>d.score-u.score||d.chunk.seq-u.chunk.seq),l.slice(0,r)}var Lh,Fh,wE,nu=C(()=>{eu();Lh=1.5,Fh=.75,wE=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(" "))});import pi from"node:fs/promises";import Wh from"node:path";async function yE(t){let e=Wh.join(t,".bostrat","events"),n;try{n=await pi.readdir(e)}catch{return ws([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await pi.readFile(Wh.join(e,o),"utf8");for(let a of s.split(`
65
+ `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return ws(r)}function bE(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 kE(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=fi(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(),d=u.length>800?u.slice(0,800)+"\u2026":u,f=`- **${l}:** ${d}`;if(i+f.length>r||(a.push(f),i+=f.length,a.length>=n))break}return a.length?["",`## ${qh}, 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 Kh(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await pi.readFile(e,"utf8");if(a.includes(qh))return{appended:!1,reason:"already-enriched"};let i=await yE(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=kE(i,bE(a),{k:r,maxChars:o,exclude:s});return c?(await pi.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 sl from"node:fs/promises";import $r from"node:path";import $T from"node:os";import{EventEmitter as OT}from"node:events";import{randomUUID as MT}from"node:crypto";var CT=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"]),NT=new Set(["user","agent","orchestrator","system","peer"]),PT=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Ht=new OT;Ht.setMaxListeners(0);var al=process.env.BOSTRAT_STATE_DIR||$r.join(process.env.HOME||$T.tmpdir(),".bostrat");function Ra(t){return t?.cwd?$r.join(t.cwd,".bostrat","events"):$r.join(al,"events")}var sm=(t,e)=>$r.join(t,`${e}.ndjson`),Ia=new Map;async function DT(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 BT(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||!CT.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!NT.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!PT.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?$r.join(al,"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=BT(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await DT(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||MT(),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 sl.mkdir(r,{recursive:!0}),await sl.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(al,"events"):Ra(t));let o;try{o=await sl.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 jT(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 Ho(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 Wo(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 LT={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"},FT={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 UT(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 il(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 cl(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 ll(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 HT(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 WT={git:t=>{let e=ut(t,new Set(["-C","-c"])),n=LT[e[0]];return n?n(e,t):null},gh:t=>{let e=ut(t),n=FT[e[0]];return n?n(e):null},sed:UT,rg:il,grep:il,egrep:il,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:cl,pnpm:cl,yarn:cl,npx:t=>{let e=ut(t)[0];return e?`Run ${e}`:null},node:t=>ll(t,"Node"),python:t=>ll(t,"Python"),python3:t=>ll(t,"Python"),make:t=>{let e=ut(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:HT,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}},qT=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function KT(t){let e=Wo(Ho(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(gt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return ul(e[2]);let n=gt(e[0]);if(qT.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=WT[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function ul(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=jT(e);for(let i=0;i<r.length-1;i++){let c=Wo(Ho(r[i])),l=Wo(Ho(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=KT(i);c&&o.push({d:c,head:gt(Wo(Ho(i))[0]||""),sub:ut(Wo(Ho(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 fl=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||ul(t)||Cr(t)}function dl(t,e){if(t){if(Array.isArray(t)){for(let n of t)dl(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&fl.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))dl(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(dl(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 ml from"node:fs/promises";import pl from"node:path";import GT from"node:os";var VT=process.env.BOSTRAT_CODEX_SESSIONS||pl.join(process.env.CODEX_HOME||pl.join(GT.homedir(),".codex"),"sessions");async function mm(t){try{return await ml.realpath(t)}catch{return t}}function zT(t=VT){let e=[],n=r=>{let o;try{o=pm.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=pl.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 JT(t,e=1048576){let n;try{n=await ml.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 XT(t){let e=await JT(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=zT();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 XT(o.file);if(s&&(s===t||await mm(s)===r))return o}return null}var YT=new Set(["output_text","text"]),ZT=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&YT.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:wl,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,hl.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let y=ZT(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 qo=new Set(["exec_command","shell","exec"]),hl=new Set(["wait","write_stdin"]),wl="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 QT(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 tA(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 eA(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=tA(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"&&qo.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=QT(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=eA(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 ml.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var ym=/^\s*─{8,}\s*$/,nA=/^❯[  ]?(.*)$/;function Ko(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(nA);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=Ko(t,n);return o&&gl(e,o)?"stuck":r||o?"submitted":"unknown"}function gl(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 rA=/^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"],yl=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${vm.join("|")})[:*\t ]*$`,"gim"),oA=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,sA=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"]}],iA=116,bl=168;function cA(t){return rA.test(String(t||"").trimStart())}function Sm(t){let e=String(t||"");if(!cA(e.trimStart()))return null;let n=e.replace(oA,"").trim(),r=lA(n),o=[];for(let u of aA){let f=u.from.map(d=>Nr(r.get(d))).find(d=>d&&!xm(d));f&&o.push({h:u.h,text:Go(f,sA)})}let s=Nr(r.get("Current Work"))||Nr(r.get("Primary Request and Intent"))||dA(n),a=Oa(s),i=Go($a(kl(a[0]||"")),iA)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Go($a(kl(l)),bl));for(let u of["Pending Tasks","Optional Next Step"]){let f=Oa(Nr(r.get(u)))[0]||"";if(!(!f||xm(f))){c.push(Go(`Left to do: ${$a(kl(f))}`,bl));break}}if(!c.length){let u=Oa(Nr(r.get("Problem Solving"))||"")[0];u&&c.push(Go($a(u),bl))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:e.length}}function lA(t){let e=new Map,n=[];yl.lastIndex=0;for(let r=yl.exec(t);r;r=yl.exec(t))n.push({name:uA(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 uA(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 dA(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 kl(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 Go(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 fA=/^Base directory for this skill:\s*(\S+)/,pA=/^Run the "([^"]+)" workflow\./,mA=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,hA=/^<local-command-(?:stdout|caveat)>/i,wA=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,gA=/^Continue from where you left off\.$/,yA=/^<task-notification>/i,bA=/<summary>([\s\S]*?)<\/summary>/i,kA=/<task-id>([\s\S]*?)<\/task-id>/i,xA=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,vA=/<status>([\s\S]*?)<\/status>/i,SA=/<result>([\s\S]*?)(?:<\/result>|$)/i,Tm=600,xl=new Set(["model","clear","compact","login","exit","quit"]);function Am(t){return xl.has(String(t||"").replace(/^\//,"").toLowerCase())}var TA=/^\/([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(fA);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=e.match(pA);if(o)return{kind:"skill",name:o[1]};let s=e.match(mA);if(s){let i=s[1].toLowerCase();return xl.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=e.match(TA);if(a&&xl.has(a[1].toLowerCase()))return{kind:"suppress"};if(hA.test(e))return{kind:"suppress"};if(gA.test(e))return{kind:"suppress"};if(wA.test(e))return{kind:"suppress"};if(yA.test(e)){let i=l=>AA(e.match(l)?.[1].trim()||"")||null,c=i(SA);return{kind:"task",summary:i(bA)||"",taskId:i(kA),toolUseId:i(xA),status:i(vA)?.toLowerCase()||null,resultHead:c?c.length>Tm?`${c.slice(0,Tm).trimEnd()}\u2026`:c:null}}return null}function AA(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 _A from"node:crypto";var vl=new Set(["cursor","codex"]),Rm=4,EA=4,IA=2,RA=24,Im=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Vo=t=>typeof t=="string"?t.trim():"";function $A(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=Vo(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,EA)){let c=Vo(i?.label);if(!c)return null;let l=Vo(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<IA)return null;n.push({question:o,header:Vo(r?.header).slice(0,RA),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Sl(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=$A(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function Tl(t,e,n){let r=_A.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Al(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=Rm}={}){if(!Array.isArray(t)||!e||!vl.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||Sl(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Tl(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=Vo(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)}}}var qh,Gh=C(()=>{nu();qh="Relevant prior discussion (verbatim, retrieved"});import ru from"node:fs/promises";import zr from"node:path";import xE from"node:os";import{EventEmitter as SE}from"node:events";import{randomUUID as vE}from"node:crypto";function hi(t){return t?.cwd?zr.join(t.cwd,".bostrat","events"):zr.join(ou,"events")}async function _E(t,e){if(!mi.has(e)){let o=(await F(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);mi.set(e,o)}let n=mi.get(e)+1;return mi.set(e,n),n}function IE(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||!TE.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!AE.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!EE.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?zr.join(ou,"events"):hi(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=IE(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await _E(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||vE(),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 ru.mkdir(r,{recursive:!0}),await ru.appendFile(Vh(r,n),JSON.stringify(i)+`
70
+ `),Yt.emit("event",i),Yt.emit(`track:${n}`,i),i}async function F(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?zr.join(ou,"events"):hi(t));let o;try{o=await ru.readFile(Vh(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 zh(t,e){let n=`track:${t}`;return Yt.on(n,e),()=>Yt.off(n,e)}var TE,AE,EE,Yt,ou,Vh,mi,ct=C(()=>{TE=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","pr.exists.offered","pr.exists.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","track.circuit.tagged","changelog.published","stall.detected","error"]),AE=new Set(["user","agent","orchestrator","system","peer"]),EE=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Yt=new SE;Yt.setMaxListeners(0);ou=process.env.BOSTRAT_STATE_DIR||zr.join(process.env.HOME||xE.tmpdir(),".bostrat");Vh=(t,e)=>zr.join(t,`${e}.ndjson`),mi=new Map});function RE(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 gs(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 ys(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}function pt(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(Xh(o)){e.has(o)&&r++;continue}n.push(o)}return n}function Zh(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 CE(t){let e=Zh(t),n=Bt(pt(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${xt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${xt(n)}`:t.includes("-i")&&n?`Edit ${xt(n)}`:null}function su(t){if(t.includes("--files")){let s=Bt(pt(t,Jh)[0]);return s&&s!=="."?`List files in ${Jr(s)}`:"List files"}let e=pt(t,Jh),n=e[0];if(!n)return null;let r=Bt(e.slice(1).find(s=>s!==".")),o=Yr(n,40);return r?`Search for \u201C${o}\u201D in ${Jr(r)}`:`Search for \u201C${o}\u201D`}function au(t){let e=pt(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 iu(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=pt(t)[0];return n?`Run ${xt(n)}`:null}function ME(t){let e=pt(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 Yh(t){return t.find(e=>/^https?:\/\//.test(e))||null}function DE(t){let e=ys(gs(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(xt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return cu(e[2]);let n=xt(e[0]);if(NE.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=PE[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function cu(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=RE(e);for(let i=0;i<r.length-1;i++){let c=ys(gs(r[i])),l=ys(gs(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(xt(c[0]))&&xt(l[0])==="sed"){let u=Zh(l),d=pt(c)[0];if(u&&d)return Yr(u.from===u.to?`Read line ${u.from} of ${xt(d)}`:`Read lines ${u.from}\u2013${u.to} of ${xt(d)}`,88)}}let o=[];for(let i of r){let c=DE(i);c&&o.push({d:c,head:xt(ys(gs(i))[0]||""),sub:pt(ys(gs(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":Yr(s?s.d:o[0].d,88)}var Yr,Bt,xt,Jr,Xh,Jh,$E,OE,PE,NE,Qh=C(()=>{Yr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Bt=t=>t&&!/[$`]/.test(t)?t:null,xt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Jr=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},Xh=t=>t.startsWith("-"),Jh=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);$E={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 ${xt(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"},OE={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"};PE={git:t=>{let e=pt(t,new Set(["-C","-c"])),n=$E[e[0]];return n?n(e,t):null},gh:t=>{let e=pt(t),n=OE[e[0]];return n?n(e):null},sed:CE,rg:su,grep:su,egrep:su,nl:t=>{let e=Bt(pt(t)[0]);return e?`Read ${xt(e)}`:null},cat:t=>{let e=Bt(pt(t)[0]);return e?`Read ${xt(e)}`:null},head:t=>{let e=Bt(pt(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${xt(e)}`:null},tail:t=>{let e=Bt(pt(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${xt(e)}`:null},wc:t=>{let e=Bt(pt(t)[0]);return e?`Count lines in ${xt(e)}`:"Count input lines"},ls:t=>{let e=Bt(pt(t)[0]);return e&&e!=="."?`List files in ${Jr(e)}`:"List files"},find:t=>Bt(t[1])&&!Xh(t[1])&&t[1]!=="."?`Find files in ${Jr(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Bt(pt(t)[0]);return e?`Create directory ${Jr(e)}`:null},touch:t=>{let e=Bt(pt(t)[0]);return e?`Create ${xt(e)}`:null},rm:t=>{let e=Bt(pt(t)[0]);return e?`Delete ${Jr(e)}`:null},mv:t=>{let e=Bt(pt(t)[0]);return e?`Move ${xt(e)}`:null},cp:t=>{let e=Bt(pt(t)[0]);return e?`Copy ${xt(e)}`:null},chmod:t=>{let e=Bt(pt(t)[1]);return e?`Change permissions on ${xt(e)}`:null},diff:t=>{let e=pt(t);return e.length>=2?`Compare ${xt(e[0])} and ${xt(e[1])}`:null},curl:t=>{let e=Yh(t);return e?`Fetch ${Yr(e,56)}`:"Fetch a URL"},wget:t=>{let e=Yh(t);return e?`Fetch ${Yr(e,56)}`:"Fetch a URL"},npm:au,pnpm:au,yarn:au,npx:t=>{let e=pt(t)[0];return e?`Run ${e}`:null},node:t=>iu(t,"Node"),python:t=>iu(t,"Python"),python3:t=>iu(t,"Python"),make:t=>{let e=pt(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:ME,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=pt(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=pt(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=pt(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},NE=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function Xr(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 Tn(t,e=""){let n=e.trim();return n||cu(t)||Xr(t)}function lu(t,e){if(t){if(Array.isArray(t)){for(let n of t)lu(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&uu.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))lu(n,e)}}}async function tw(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Jt(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(lu(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Tn(d.command,d.description),ts:u})}return o.slice(-n)}function ew(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,w)=>w>=s&&(m.ts||0)>(l.ts||0)),d=u===-1?r.length:u,f=r.slice(s,d);if(s=d,!f.length)continue;let p=f.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands: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}var uu,Zr=C(()=>{he();Qh();uu=new Set(["Bash"])});import nw from"node:fs";import fu from"node:fs/promises";import du from"node:path";import BE from"node:os";async function rw(t){try{return await fu.realpath(t)}catch{return t}}function LE(t=jE){let e=[],n=r=>{let o;try{o=nw.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=du.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:nw.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function FE(t,e=1048576){let n;try{n=await fu.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 UE(t){let e=await FE(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 ar(t,e){if(!t&&!e)return null;let n=LE();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await rw(t);for(let o of n){let s=await UE(o.file);if(s&&(s===t||await rw(s)===r))return o}return null}async function ow(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await ar(t,e);if(!o)return[];let s;try{s=await Jt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let f of s.split(`
77
+ `)){let p=f.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,d=m.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:mu,sourceId:m.payload?.turn_id||d||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let w=m.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,pu.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let b=WE(w.content),k=c.length?c.slice():null;c=[],!(!b&&!k)&&a.push({role:"assistant",text:b,tools:k||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let f of a)f.model||(f.model=i);return a.slice(-n)}function qE(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 KE(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+=`
78
+ `;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 GE(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=KE(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function sw(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await ar(t,e);if(!o)return[];let s;try{s=await Jt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
79
+ `)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&bs.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=qE(p);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=GE(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((p,m)=>{a.push({id:f?m?`${f}:${m}`:f:null,tool:u.name,command:p,description:"",summary:Tn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function aw(t,e){let n=await ar(t,e);if(!n)return null;try{return Math.round((await fu.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var jE,HE,WE,bs,pu,mu,Qr=C(()=>{he();Zr();jE=process.env.BOSTRAT_CODEX_SESSIONS||du.join(process.env.CODEX_HOME||du.join(BE.homedir(),".codex"),"sessions");HE=new Set(["output_text","text"]),WE=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&HE.has(e.type)).map(e=>e.text).join(`
80
+ `).trim():"";bs=new Set(["exec_command","shell","exec"]),pu=new Set(["wait","write_stdin"]),mu="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 ks(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(iw.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(iw.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(VE);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 _t(t){return String(t||"").replace(/\s+/g," ").trim()}function cw(t,e,{slackMs:n=2e4}={}){let r=_t(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&&_t(o.text).includes(r))}function lw(t,e,n,{busy:r=!1}={}){let o=ks(t,n);return o&&hu(e,o)?"stuck":r||o?"submitted":"unknown"}function hu(t,e){let n=_t(t),r=_t(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var iw,VE,wu=C(()=>{iw=/^\s*─{8,}\s*$/,VE=/^❯[  ]?(.*)$/});function QE(t){return zE.test(String(t||"").trimStart())}function fw(t){let e=String(t||"");if(!QE(e.trimStart()))return null;let n=e.replace(JE,"").trim(),r=t_(n),o=[];for(let u of XE){let d=u.from.map(f=>to(r.get(f))).find(f=>f&&!uw(f));d&&o.push({h:u.h,text:xs(d,YE)})}let s=to(r.get("Current Work"))||to(r.get("Primary Request and Intent"))||n_(n),a=gi(s),i=xs(wi(bu(a[0]||"")),ZE)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(xs(wi(bu(l)),yu));for(let u of["Pending Tasks","Optional Next Step"]){let d=gi(to(r.get(u)))[0]||"";if(!(!d||uw(d))){c.push(xs(`Left to do: ${wi(bu(d))}`,yu));break}}if(!c.length){let u=gi(to(r.get("Problem Solving"))||"")[0];u&&c.push(xs(wi(u),yu))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:e.length}}function t_(t){let e=new Map,n=[];gu.lastIndex=0;for(let r=gu.exec(t);r;r=gu.exec(t))n.push({name:e_(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 e_(t){return dw.find(n=>n.toLowerCase()===t.toLowerCase())||t}function to(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 n_(t){let e=t.replace(/^[\s\S]*?\bSummary:\s*/i,"");return to(e.split(/\n{2,}/).slice(0,2).join(" "))}function gi(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 bu(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 wi(t){return String(t||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,e=>`\u2026/${e.split("/").slice(-2).join("/")}`)}function uw(t){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(t||"").trim())}function xs(t,e){let n=String(t||"").trim();if(n.length<=e)return n;let r=gi(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 zE,dw,gu,JE,YE,XE,ZE,yu,pw=C(()=>{zE=/^This session is being continued from a previous conversation that ran out of context\./,dw=["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"],gu=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${dw.join("|")})[:*\t ]*$`,"gim"),JE=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,YE=420,XE=[{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"]}],ZE=116,yu=168});function hw(t){return ku.has(String(t||"").replace(/^\//,"").toLowerCase())}function yi(t){let e=String(t||"").trim();if(!e)return null;let n=fw(e);if(n)return n;let r=e.match(r_);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=e.match(o_);if(o)return{kind:"skill",name:o[1]};let s=e.match(s_);if(s){let i=s[1].toLowerCase();return ku.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=e.match(h_);if(a&&ku.has(a[1].toLowerCase()))return{kind:"suppress"};if(a_.test(e))return{kind:"suppress"};if(c_.test(e))return{kind:"suppress"};if(i_.test(e))return{kind:"suppress"};if(l_.test(e)){let i=l=>w_(e.match(l)?.[1].trim()||"")||null,c=i(m_);return{kind:"task",summary:i(u_)||"",taskId:i(d_),toolUseId:i(f_),status:i(p_)?.toLowerCase()||null,resultHead:c?c.length>mw?`${c.slice(0,mw).trimEnd()}\u2026`:c:null}}return null}function w_(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function ww(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 gw(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":t?.kind==="resume-summary"?"context-resumed":"command-run"}var r_,o_,s_,a_,i_,c_,l_,u_,d_,f_,p_,m_,mw,ku,h_,xu=C(()=>{pw();r_=/^Base directory for this skill:\s*(\S+)/,o_=/^Run the "([^"]+)" workflow\./,s_=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,a_=/^<local-command-(?:stdout|caveat)>/i,i_=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,c_=/^Continue from where you left off\.$/,l_=/^<task-notification>/i,u_=/<summary>([\s\S]*?)<\/summary>/i,d_=/<task-id>([\s\S]*?)<\/task-id>/i,f_=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,p_=/<status>([\s\S]*?)<\/status>/i,m_=/<result>([\s\S]*?)(?:<\/result>|$)/i,mw=600,ku=new Set(["model","clear","compact","login","exit","quit"]);h_=/^\/([a-z0-9:_-]+)$/i});import g_ from"node:crypto";function x_(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,bw)){let o=Ss(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,y_)){let c=Ss(i?.label);if(!c)return null;let l=Ss(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<b_)return null;n.push({question:o,header:Ss(r?.header).slice(0,k_),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function vu(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;yw.lastIndex=0;let e=null;for(let o;o=yw.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=x_(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function Tu(t,e,n){let r=g_.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Au(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=bw}={}){if(!Array.isArray(t)||!e||!Su.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||vu(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Tu(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 kw(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=Ss(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 Su,bw,y_,b_,k_,yw,Ss,xw,Eu=C(()=>{Su=new Set(["cursor","codex"]),bw=4,y_=4,b_=2,k_=24,yw=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Ss=t=>typeof t=="string"?t.trim():"";xw=`
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,104 @@ 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 OA(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 MA=new Set(["opened","ci-fixed","reconciled","pushed"]),Mm=t=>`${t.repo??""}#${t.number}`;function CA(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(OA(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=>!hl.has(at)&&!qo.has(at)):w.tools||[],J=w.text,W=null;if(vl.has(g.provider)){let at=Sl(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:Tl(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,MA.has(w.change)&&w.number!=null){let E=CA(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 PA(e.sort((g,w)=>(g.ts??0)-(w.ts??0)))}var NA=2*6e4;function PA(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<=NA){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:DA(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 DA(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 BA=12e4,jA=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)+BA,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&jA.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var LA=2500,FA=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"||!FA.has(r.type)||!r.box)continue;let o=(r.ts??0)+LA,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 UA=12,HA=2e3,Pm=48e3,WA=30,qA=10;function KA(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 GA=(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 VA(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 zA(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<WA&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<qA&&(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=KA(t),s=VA(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(-UA);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=zA(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"}:** ${GA(d.text,HA)}`),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)}
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(/�+$/,"")}
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",XA="/clear",YA=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.`,ZA=`${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",QA=".bostrat/arc",t_="beat.txt",Fm=200,Ym=4,Zm=5,e_=36,n_=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+/,r_=(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 o_(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=r_(s.replace(Um,"").replace(/[.;,:]+$/,""),Zm,e_);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,QA),o=wn.join(r,t_);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,n_(o)),await ch(o,s,n),o_(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===XA||n.startsWith(zm)||n.startsWith(Jm):!1}var s_=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-s_;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 a_=3500,i_=3e5,El=new Map;function Pa(t){return El.get(t)||null}async function oh(t){let e=El.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 El.set(t,r),c_(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function c_(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(a_),t.step="resume",await Na(t.name,n,ZA),t.step="done",t.finishedAt=Date.now()}async function sh(t,e,{timeoutMs:n=i_}={}){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,YA(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 l_(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 l_(t,e){try{if(!JA.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 Os}from"node:crypto";import{execFile as nO}from"node:child_process";import{promisify as rO}from"node:util";import Zw from"node:path";var Il={"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"},u_=/^\d{4}-\d{2}-\d{2}$/;function dh(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!u_.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 d_(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 f_=d_(Il);function p_(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 Il[e]?e:uh[e]?uh[e]:Object.keys(Il).find(r=>e.startsWith(r))||null}function fh(t,e){let n=m_(t);if(!n)return null;let r=p_(e);return r===null?null:f_[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 h_(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=h_(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 b_}from"node:crypto";import{execFile as k_}from"node:child_process";import{promisify as x_}from"node:util";import{chromium as v_}from"playwright-core";import Rl from"node:os";import{execFile as w_}from"node:child_process";import{promisify as g_}from"node:util";var y_=g_(w_);async function Ba(){if(process.platform!=="darwin")return Rl.freemem();try{let{stdout:t}=await y_("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:Rl.freemem()}catch{return Rl.freemem()}}var Ml=x_(k_),bh=process.env.BOSTRAT_STATE_DIR||St.join(process.env.HOME||yh.tmpdir(),".bostrat"),kh=St.join(bh,"mocks.json"),S_=process.env.BOSTRAT_MOCK_ENGINE||"chromium",T_=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",A_=process.env.BOSTRAT_FIREFOX_BIN||"firefox",__=1300,Dr=2,E_=32,xh=1500,Br=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,I_=700*1024*1024,wh=100*1024*1024,R_=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",$_=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",vh=2e4,se=class extends Error{constructor(e,n=""){super(n?`${e}
111
- ${n.trim().split(`
112
- `).slice(-6).join(`
113
- `)}`:e),this.name="MockRenderError"}},Jn=null,$l=null;async function xe(){return Jn||($l||=(async()=>{try{Jn=JSON.parse(await kt.readFile(kh,"utf8"))}catch{Jn={mocks:[]}}finally{$l=null}return Jn.mocks||=[],Jn})(),$l)}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"?zo(t,r,"video"):r&&zo(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 Jo=t=>St.join(t,".bostrat","mocks");function ee(t,e){let n=Jo(t.cwd);return{html:St.join(n,`${t.id}.v${e}.html`),png:St.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?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 Cl(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var O_=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(O_)}function M_(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 C_(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 N_({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 P_(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 D_(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 B_({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await v_.launch({executablePath:T_,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 P_(a,i),u=l&&N_({...l,pad:E_*Dr,align:Dr});u&&(i=await D_(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<xh)throw new se(`capture produced a ${i.length}-byte image`);let c=Cl(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 Ml(A_,["--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=Cl(await C_(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 j_(t){if(S_==="firefox")return gh(t);try{return await B_(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return gh(t);throw e}}var L_=j_;var Ol=Promise.resolve();function Th(t){return Ol=Ol.then(t,t),Ol}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<I_)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 L_({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}Nl(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 Nl(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 F_({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?__: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=M_(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=F_({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:b_().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(Jo(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(Jo(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 U_(t){try{let{stdout:e}=await Ml(R_,["-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 H_(t,e){try{await Ml($_,["-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=Jo(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 U_(s),c=await H_(s,a);Nl(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=Cl(n);if(!r)throw new Error("image must be a PNG");let o=ee(t,e);await kt.writeFile(o.png,n),Nl(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=Jo(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 Yo from"node:fs/promises";import Wt from"node:path";import W_ from"node:os";import q_ from"node:net";import{randomUUID as K_}from"node:crypto";import{execFile as G_}from"node:child_process";import{promisify as V_}from"node:util";var Se=V_(G_),z_=process.env.BOSTRAT_STATE_DIR||Wt.join(process.env.HOME||W_.tmpdir(),".bostrat"),Fh=Wt.join(z_,"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,J_="bostrat-view-",jr=t=>`${J_}${t}`,jh=new Set(["bostrat","www","api","views","app"]),La=t=>Object.assign(new Error(t),{status:400}),X_=new Set(["npm","pnpm","yarn","bun"]),Bl="package.json",Y_=/[|;&`]|\$\(/,Z_=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Q_=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Uh=t=>String(t).trim().split(/\s+/).filter(Boolean),tE=/^[A-Za-z_][A-Za-z0-9_]*=/;function eE(t){for(let e of Uh(t))if(!tE.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 nE(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 rE=t=>Yo.stat(t).then(e=>e.isDirectory(),()=>!1),Wh=t=>Yo.stat(t).then(e=>e.isFile(),()=>!1),Fa=t=>Yo.realpath(t).catch(()=>Wt.resolve(t));async function oE(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 sE(t){let n=(await Yo.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,Bl))?`./${o.name}`:null))).filter(Boolean)}async function aE(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 iE(t,e){if(!await rE(t))throw La(`serve directory does not exist: ${t}`);let n=eE(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(Y_.test(e)||Z_.has(n)||!X_.has(n)||!Hh(e).includes("run")||Q_.test(e))return;let r=nE(t,e);if(await Wh(Wt.join(r,Bl)))return;let o=await sE(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw La(`no ${Bl} in ${r}${s}`)}var ve=null,Pl=null;async function Fe(){return ve||(Pl||=(async()=>{try{ve=JSON.parse(await Yo.readFile(Fh,"utf8"))}catch{ve={views:[],adoptWatermarks:{}}}finally{Pl=null}return ve.views||=[],ve.adoptWatermarks||={},ve})(),Pl)}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 Xo=new Map,Fl=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,qh=12e3;function Ul(t){return Xo.get(t)||null}function Kh(t,{name:e,box:n}={}){let r=Xo.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 Xo.set(t,o),o}function jl(t,e){let n=Xo.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ua(t,{error:e=null}={}){let n=Xo.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 Ll(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function cE(t,e){let n=Ll(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 Zo(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=q_.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 Hl(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 lE(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 Zo(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Dh}-${Bh}`)}async function uE(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 Qo(t){try{await Se(C,P("kill-session","-t",jr(t)))}catch{}}async function Wl(t){if(!await Po())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(`
105
+ `});function S_(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}}function T_(t,e,n){let r=Sw(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 ir(t){let e=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,d=new Map;for(let y of t||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let h=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let T of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let _=T[0].split("/").pop().toLowerCase();d.has(_)||d.set(_,[]),d.get(_).push(y.payload?.ts??y.ts)}}let f=3*6e4,p=20*6e4,m=(y,h)=>!!(y?.name&&d.get(String(y.name).toLowerCase()))?.some(_=>_>=h-f&&_<=h+p);for(let y of t||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let h=_t(y.payload?.text);h&&(o.has(y.boxId)||o.set(y.boxId,[]),o.get(y.boxId).push(h))}else if(y.type==="turn.user"&&y.source==="orchestrator"){let h=_t(y.payload?.text);h&&(s.has(y.boxId)||s.set(y.boxId,new Set),s.get(y.boxId).add(h))}else if(y.type==="job.started"&&y.payload?.id)a.add(y.payload.id),(y.payload.kind||"agent")==="agent"&&y.payload.name&&c.add(y.payload.name);else if(y.type==="turn.user"&&y.payload?.note?.kind==="task"){let h=y.payload.note,T={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:et(y)};h.toolUseId&&i.set(h.toolUseId,T);let _=String(h.summary||"").match(u);_&&l.set(_[1],T)}let w=new Map,b=new Map,k=null,S=new Map,$=new Map,R=new Map,J=new Map,O=new Map,D=new Map,A=new Map,P=new Map,q=new Map,at=new Map,ot=new Map,v=()=>{for(let y of ot.values())y.questions.length&&e.push(y);ot.clear()},M=null,Rt=6e4,Kt=new Map,yt=(y,h)=>{let T=o.get(y);if(!T?.length)return!1;let _=_t(h),wt=s.get(y);return T.some(lt=>{if(!_.startsWith(lt))return!1;let ut=_t(_.slice(lt.length));return!!ut&&!!wt?.has(ut)})};for(let y of t||[]){let h=y.payload||{};switch(y.type){case"turn.user":{if(y.source==="hook"||!String(h.text||"").trim()&&!h.images?.length)break;{let T=h.note||yi(h.text);if(T){if(T.kind==="task"){if(T.toolUseId&&a.has(T.toolUseId))break;let _=String(T.summary||"").match(u);if(!T.toolUseId&&_&&c.has(_[1]))break}if(T.kind==="command"&&hw(T.command))break;if(T.kind==="resume-summary"){e.push({id:y.id,kind:"resume-summary",title:T.title||"Context resumed from summary",bullets:T.bullets||[],sections:T.sections||[],rawChars:T.rawChars??null,ts:et(y),box:y.boxId,agent:y.provider});break}T.kind!=="suppress"&&e.push({id:y.id,kind:"event",type:gw(T),name:T.name??null,command:T.command??null,summary:T.summary??null,status:T.status??null,ts:et(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${et(y)}`)||y.source==="transcript"&&yt(y.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){e.push({id:y.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:et(y),box:y.boxId,agent:y.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(M=null);break}for(let T of S.values())T.status="stale";S.clear();for(let T of R.values())T.status="stale";R.clear(),M&&M.box===y.boxId&&(e.push(S_(M)),M=null),e.push({id:y.id,kind:"message",role:"user",text:h.text,ts:et(y),box:y.boxId,agent:y.provider,boxRole:h.boxRole,...y.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let T=h.from||{};e.push({id:y.id,kind:"event",type:"peer-message",text:h.text,from:T.trackName||T.boxId||T.name||null,fromTrackId:T.trackId||null,ts:et(y),box:y.boxId,agent:y.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;v();let T=(h.images||[]).filter(ut=>!m(ut,et(y)));if(!String(h.text||"").trim()&&!T.length)break;if(y.provider==="cursor"){let ut=`${y.boxId}|${h.text}`;if(n.has(ut))break;n.add(ut)}(!M||M.box===y.boxId)&&(M=null);let _=y.provider==="codex"?(h.tools||[]).filter(ut=>!pu.has(ut)&&!bs.has(ut)):h.tools||[],wt=h.text,lt=null;if(Su.has(y.provider)){let ut=vu(String(h.text||""));ut&&(wt=ut.text,lt={questions:ut.questions})}e.push({id:y.id,kind:"message",role:"assistant",text:wt,ts:et(y),box:y.boxId,agent:y.provider,boxRole:h.boxRole,...lt?{ask:lt}:{},..._.length?{tools:_}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...T.length?{images:T}:{}}),lt&&(M={id:y.id,groupId:Tu(y.provider,h.sourceId||y.id,lt.questions),provider:y.provider,box:y.boxId,questions:lt.questions,ts:et(y)});break}case"job.started":{if(!h.id)break;let T=et(y),_=i.get(h.id);if(!_&&(h.kind||"agent")==="agent"&&h.name){let j=l.get(h.name);j&&j.ts>=T&&(_=j)}let wt={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:_?_.status==="killed"?"stopped":_.status&&_.status!=="completed"?"failed":"done":"running",startedTs:T,finishedTs:_?.ts??null,summary:_?.summary??null,resultHead:_?.resultHead??null},lt=Kt.get(y.boxId),ut=lt?.jobs[lt.jobs.length-1]?.startedTs;if(lt&&ut!=null&&T-ut<=Rt)lt.jobs.push(wt);else{let j={id:y.id,kind:"job-dock",dockId:h.id,ts:T,box:y.boxId,agent:y.provider,jobs:[wt]};Kt.set(y.boxId,j),e.push(j)}break}case"handoff.done":e.push(Ie(y,"lead-handoff"));break;case"subtask.delegated":e.push({...Ie(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":e.push(Ie(y,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let _ of S.values())_.status="stale";S.clear();for(let _ of R.values())_.status="stale";R.clear()}let T=Ie(y,`pr-${h.change}`);if(T.box=y.boxId??null,v_.has(h.change)&&h.number!=null){let _=T_(w,b,h);if(w.size>=2){for(let lt of b.values())for(let ut of lt)ut.grouped=!0;T.groupPrs=[...w.values()];let wt=[T];for(let lt of w.keys())b.set(lt,wt)}else{let wt=b.get(_)??[];wt.push(T),b.set(_,wt)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let _=Sw(h),wt=k&&!k.complete||w.size>=2;if(w.has(_))w.delete(_),b.delete(_);else if(h.number!=null)for(let lt of[...w.keys()])lt.endsWith(`#${h.number}`)&&(w.delete(lt),b.delete(lt));if(wt&&h.number!=null){(!k||k.complete)&&(k={id:y.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:et(y),ts:et(y),box:y.boxId,agent:y.provider},e.push(k)),k.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:et(y)}),k.remaining=[...w.values()],k.complete=w.size===0;break}}e.push(T);break}case"pr.offered":{if(!h.offerId)break;for(let _ of S.values())_.status="stale";S.clear();for(let _ of R.values())_.status="stale";R.clear();let T={id:y.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:et(y),box:y.boxId,agent:y.provider};S.set(h.offerId,T),$.set(h.offerId,T),e.push(T);break}case"pr.offer.answered":{let T=$.get(h.offerId);T&&(T.status=h.action||"dismiss",T.actions=Array.isArray(h.actions)?h.actions:null,T.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(T.existingPr={number:h.existingPr.number??null}),S.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let _ of R.values())_.status="stale";R.clear();let T={id:y.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:et(y),box:y.boxId,agent:y.provider};R.set(h.offerId,T),J.set(h.offerId,T),e.push(T);break}case"pr.exists.answered":{let T=J.get(h.offerId);T&&(T.status=h.choice||"dismiss",R.delete(h.offerId));break}case"box.state.changed":{let T=h.change==="model"?"model-change":h.change;if(!T)break;let _=Ie(y,T);T==="model-change"&&(_.to=h.model),e.push(_);break}case"view.state.changed":{if(h.change!=="created"||h.restarted)break;e.push({id:y.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:et(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{e.push({id:y.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:et(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(h.change==="picked"){e.push({id:y.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:et(y),box:y.boxId,agent:y.provider});break}if(h.change!=="created")break;e.push({id:y.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:et(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(h.change!=="created")break;e.push({id:y.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:et(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;e.push({id:y.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:et(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{e.push({id:y.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:et(y),box:y.boxId,agent:y.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let _ of A.values())_.box===(h.box||y.boxId)&&(_.status="stale",A.delete(_.offerId));let T={id:y.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:et(y),box:h.box||y.boxId,agent:h.agent||y.provider};A.set(h.offerId,T),P.set(h.offerId,T),e.push(T);break}case"box.fullscreen.answered":{let T=P.get(h.offerId);T&&(T.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",A.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let _ of O.values())_.key===h.key&&(_.status="stale",O.delete(_.offerId));let T={id:y.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:et(y),box:y.boxId,agent:y.provider};O.set(h.offerId,T),D.set(h.offerId,T),e.push(T);break}case"ticket.move.answered":{let T=D.get(h.offerId);T&&(T.status=h.action==="moved"?"moved":"dismiss",O.delete(h.offerId));break}case"ticket.state.changed":{for(let T of O.values())T.key===h.key&&T.to===h.to&&(T.status="stale",O.delete(T.offerId));if(h.mode==="offer")break;e.push(Ie(y,"ticket-state"));break}case"ticket.commented":{e.push({id:y.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:et(y),box:y.boxId,agent:y.provider});break}case"ticket.flow.changed":e.push(Ie(y,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let _ of q.values())_.status="stale";q.clear();let T={id:y.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:et(y),box:y.boxId,agent:y.provider};q.set(h.proposalId,T),at.set(h.proposalId,T),e.push(T);break}case"ticket.create.answered":{let T=at.get(h.proposalId);T&&(T.status=h.action==="created"?"created":"dismiss",T.createdCount=h.createdCount??null,T.failedCount=h.failedCount??null,q.delete(h.proposalId));break}case"ticket.created":{let T={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},_=h.proposalId?at.get(h.proposalId):null;if(_){_.created=T;break}e.push({id:y.id,kind:"ticket-created",...T,ts:et(y),box:y.boxId,agent:y.provider});break}case"error":{if(!h.kind)break;e.push(Ie(y,h.kind));break}case"usage.cap.hit":e.push(Ie(y,"usage-cap-hit"));break;case"usage.cap.warning":e.push(Ie(y,"usage-cap-warning"));break;case"provider.connected":{e.push({id:y.id,kind:"event",type:"provider-connected",provider:h.provider||y.provider,ts:et(y),at:et(y),box:y.boxId,agent:y.provider});break}case"prompt.answered":{if(M=null,h.control||!h.question)break;let T=h.groupId||h.promptId;if(!T)break;let _=ot.get(T);_||(_={id:y.id,kind:"prompt",groupId:T,provider:y.provider,box:y.boxId,agent:y.provider,questionTotal:h.questionTotal??1,questions:[],ts:et(y)},ot.set(T,_));let wt=h.questionIndex??0,lt={index:wt,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},ut=_.questions.findIndex(j=>j.index===wt&&_t(j.question)===_t(lt.question));ut>=0?_.questions[ut]=lt:_.questions.push(lt),_.questions.sort((j,L)=>j.index-L.index),_.questionTotal=Math.max(_.questionTotal,h.questionTotal??1),_.questions.length>=_.questionTotal&&(e.push(_),ot.delete(T));break}default:break}}return E_(e.sort((y,h)=>(y.ts??0)-(h.ts??0)))}function E_(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<=A_){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 _u(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:__(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??ir(t)).filter(o=>o.kind==="message");return ew(n,r)}function __(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Xr(t.command)?e:Tn(t.command,t.description||"")}function Ie(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:et(t),ts:et(t)}}function vw(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)+I_,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}function Tw(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||!O_.has(r.type)||!r.box)continue;let o=(r.ts??0)+$_,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 et,v_,Sw,A_,I_,R_,$_,O_,Iu=C(()=>{Zr();Qr();wu();xu();Eu();et=t=>t.payload?.ts??t.ts;v_=new Set(["opened","ci-fixed","reconciled","pushed"]),Sw=t=>`${t.repo??""}#${t.number}`;A_=2*6e4;I_=12e4,R_=new Set(["archived","unarchived","opened"]);$_=2500,O_=new Set(["pr-opened","pr-merged","pr-auto-armed"])});function D_(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}}function j_(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 L_(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<P_&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<N_&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Iw(t,{trackName:e="this Track",now:n=null,mode:r="failover"}={}){let o=D_(t),s=j_(t),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=ir(t??[]).filter(f=>f?.kind==="message"&&typeof f.text=="string"&&f.text.trim()).slice(-C_);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${e}`),l.push(""),l.push(_w[r]??_w.failover),l.push(""),o.lastPush?.sha){let f=Ew(n,o.lastPush.ts),p=f?` (~${f} 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 f=Ew(n,i);l.push(`## Last real checkpoint (written by the previous agent${f?`, ~${f} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=L_(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(f=>`\`${f}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(f=>`\`${f}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let f of c)l.push(`**${f.role==="user"?"operator":"agent"}:** ${B_(f.text,M_)}`),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 d=l.join(`
106
+ `);return d.length<=Aw?d:`${d.slice(0,Aw)}
107
+
108
+ [\u2026 brief truncated]`}var C_,M_,Aw,P_,N_,B_,Ew,_w,Rw=C(()=>{Iu();C_=12,M_=2e3,Aw=48e3,P_=30,N_=10;B_=(t,e)=>{let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1)}\u2026`},Ew=(t,e)=>t&&e?Math.max(1,Math.round((t-e)/6e4)):null;_w={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."}});import F_ from"node:fs";import An from"node:fs/promises";import En from"node:path";function Dw(t){let e=er(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=$w?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,$w).toString("utf8").replace(/�+$/,"")}
109
+
110
+ [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function J_(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Cw.test(s)){if(r.length>=Hw)continue;let a=z_(s.replace(Cw,"").replace(/[.;,:]+$/,""),Ww,G_);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>Ow&&(o=`${o.slice(0,Ow-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function qw(t,e,{timeoutMs:n=9e4}={}){let r=En.join(e,q_),o=En.join(r,K_);await An.mkdir(r,{recursive:!0});let s=await bi(o),a=await tt(t);if(!a)throw new Error(`session "${t}" not found`);return await ki(t,a,V_(o)),await Qw(o,s,n),J_(await An.readFile(o,"utf8"))}function Kw(t,e){let n=(e||"").trim();if(!n)return!1;if(t==="assistant")return n===jw||n===Uw;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(Bw)||n===U_||r.startsWith(Lw)||r.startsWith(Fw)}function Gw(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-Y_;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 Vw(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 zw(t,{windows:e=[]}={}){if(Kw(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}function xi(t){return Ru.get(t)||null}async function Jw(t){let e=Ru.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:En.join(n.cwd,Pw,Nw)};return Ru.set(t,r),Q_(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function Q_(t,e){t.step="summarize",await Yw(t.name,e),t.step="clear";let n=await tt(t.name);await ki(t.name,n,"/clear"),await $u(X_),t.step="resume",await ki(t.name,n,W_),t.step="done",t.finishedAt=Date.now()}async function Yw(t,e,{timeoutMs:n=Z_}={}){let r=En.join(e,Pw),o=En.join(r,Nw);await An.mkdir(r,{recursive:!0});let s=await bi(o),a=await tt(t);await G({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:o,beforeMtime:s}),await ki(t,a,H_(o)),await Qw(o,s,n),await G({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:o,afterMtime:await bi(o)});let i=await Kh(e,o,{exclude:u=>Kw(u.role,u.text)});await G({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 An.readFile(o,"utf8");await I({id:c,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Dw(u),ts:Date.now()}})}catch{}}await Wm(e);let l=await tI(r,o);return c&&await th(c,l||o,{box:t,agent:a?.agent??null}),o}async function Xw(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 Yw(t,n.cwd,e?{timeoutMs:e}:void 0)}async function Zw(t,e,{trackName:n="this Track"}={}){let r=await F({id:t,cwd:e}),o=Iw(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=En.join(e,Mw);return await An.mkdir(En.dirname(s),{recursive:!0}),await An.writeFile(s,o,"utf8"),await I({id:t,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Dw(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Mw}async function bi(t){try{return(await An.stat(t)).mtimeMs}catch{return 0}}async function Qw(t,e,n){let r=Date.now()+n;for(;;){if(await bi(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await $u(2e3)}}async function tI(t,e){try{if(!F_.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=En.join(t,`${n}.md`);return await An.copyFile(e,r),r}catch{return null}}function $u(t){return new Promise(e=>setTimeout(e,t))}async function ki(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 vn({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 vt(t,["Escape"],{kind:"drive-wipe"}),await $u(150),await Kr(t,ei,{kind:"drive-wipe"}),await Mt(t,n,{kind:"orchestration-drive",..._e(r.agent)})}var Pw,Nw,$w,Bw,jw,Lw,U_,H_,W_,Fw,Uw,q_,K_,Ow,Hw,Ww,G_,V_,Cw,z_,Y_,X_,Z_,Ru,Mw,Ou=C(()=>{Ut();di();He();ms();Gh();Ka();ct();Cl();Rw();Pw=".bostrat/handoff",Nw="latest.md",$w=32*1024;Bw="Checkpoint: write a work-in-progress handoff",jw="HANDOFF WRITTEN",Lw="Resume from .bostrat/handoff/latest.md",U_="/clear",H_=t=>`${Bw} 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 ${jw} and take no further action.`,W_=`${Lw}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Fw="Beat: write a",Uw="BEAT WRITTEN",q_=".bostrat/arc",K_="beat.txt",Ow=200,Hw=4,Ww=5,G_=36,V_=t=>`${Fw} 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 ${Hw} further lines, each starting with "- " and naming ONE piece in at most ${Ww} 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 ${Uw} and take no further action.`,Cw=/^(?:[-–—*•·]|\d+[.)])\s+/,z_=(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};Y_=6e5;X_=3500,Z_=3e5,Ru=new Map;Mw=".bostrat/handoff/status-latest.md"});function eg(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!eI.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 nI(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:eg(o.from,"from",n),toMs:eg(o.to,"to",n)}))}return e}function oI(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 sI(t){if(!t)return null;let e=String(t).trim();return Cu[e]?e:tg[e]?tg[e]:Object.keys(Cu).find(r=>e.startsWith(r))||null}function ng(t,e){let n=sI(t);if(!n)return null;let r=oI(e);return r===null?null:rI[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function _n(t,e={},n){let r=ng(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 Cu,tg,eI,rI,rg,vs=C(()=>{Cu={"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)."}]},tg={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"},eI=/^\d{4}-\d{2}-\d{2}$/;rI=nI(Cu);rg=(t,e)=>ng(t,e)!==null});function aI(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function sg(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function qe(t){let e=t.payload?.model||"unknown",n=aI(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=t.source==="vendor",s=r,a=!1;if(n&&!o){let l=_n(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&&!rg(e,t.ts);return{usd:s,model:e,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function In(t,{boxLabels:e={}}={}){let n=(t||[]).filter(f=>f.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=og();for(let f of n){let{usd:p,model:m,tokens:w,repriced:b,unpriced:k,asRecorded:S}=qe(f);i+=S;let $=f.provider||"unknown",R=f.boxId||"unattributed";if(b&&c++,k&&(l++,u.add(m)),a+=p,sg(d,w),r[m]||={usd:0,tokens:og(),events:0},r[m].usd+=p,r[m].events+=1,sg(r[m].tokens,w),o[$]||={usd:0,events:0},o[$].usd+=p,o[$].events+=1,s[R]||={usd:0,events:0,provider:$,models:new Set,label:e[R]||R},s[R].usd+=p,s[R].events+=1,m!=="unknown"&&s[R].models.add(m),f.epoch!=null){let J=(s[R].epochs||={})[f.epoch]||={usd:0,events:0,provider:$,models:new Set};J.usd+=p,J.events+=1,J.provider=$,m!=="unknown"&&J.models.add(m)}}return{totalUsd:eo(a),asRecordedUsd:eo(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Si(r,f=>({...f,usd:eo(f.usd)})),byProvider:Si(o,f=>({...f,usd:eo(f.usd)})),byBox:Si(s,f=>({usd:eo(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:Si(f.epochs,p=>({usd:eo(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var og,eo,Si,cr=C(()=>{vs();og=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});eo=t=>Math.round(t*1e4)/1e4,Si=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]))});function iI(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 ag(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 d=u.payload||{};!d.repo||d.number==null||(d.change==="merged"?o.set(`${d.repo}#${d.number}`,d):d.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(d=>{let[f,p]=d.split("#");return`${f.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Mu(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Mu(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(d=>`${d.fromAgent}\u2192${d.toAgent}`).join(", ");n.push(`${Mu(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=In(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?iI(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Mu,ig=C(()=>{cr();Mu=(t,e)=>`${t} ${e}${t===1?"":"s"}`});import{execFile as cI}from"node:child_process";import{promisify as lI}from"node:util";import{randomUUID as uI}from"node:crypto";async function Pu(t,e){let n=Date.now()+e;for(;;){if(await qr(t))return!0;if(Date.now()>n)return!1;await vi(fI)}}async function mI(t,e){let n=lg[e]||lg.default;n.escape&&(await vt(t,["Escape"],{kind:"swap-exit"}),await vi(150)),n.wipe&&await Kr(t,ei,{kind:"swap-exit"}),await ni(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=_e(e).submitDelayMs||0;await vi(Math.max(r,250)),await vt(t,["Enter"],{kind:"swap-exit"})}async function hI(t,e){try{let{stdout:n}=await no(N,B("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ug(t){await no(N,B("respawn-pane","-k","-t",t))}async function Nu(t,e={}){if(!kt(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||bn(n),a=Number(e.exitTimeoutMs)||dI;await G({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await qr(t)&&(await mI(t,o),!await Pu(t,a)&&(i=!0,await G({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await ug(t),!await Pu(t,cg))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&nr(n),l=c?Da():null,u=await hI(t,"ANTHROPIC_API_KEY"),d=!1;try{l?(await no(N,B("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await no(N,B("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),d=!0)}catch(b){throw l?Ya(b,l):b}if(d&&!i&&(await ug(t),!await Pu(t,cg)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,p=e.resumeSessionId||uI(),m=f?n:jl(n,p,t),w=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await pn(r.cwd),s==="codex"&&await Nr(r.cwd),s==="cursor"&&await Dr(r.cwd),await or(t),await no(N,B("set-option","-t",t,"@bostrat-sid",p)),await no(N,B("set-option","-t",t,"@bostrat-agent",s)),await vi(pI),await Mt(t,m,{kind:"swap-launch"}),await G({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:d}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:d}}var no,vi,dI,cg,fI,pI,lg,dg=C(()=>{Ut();Xo();ja();La();ms();He();no=lI(cI),vi=t=>new Promise(e=>setTimeout(e,t)),dI=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,cg=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,fI=250,pI=400,lg={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 fg(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Jt(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
111
+ `)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let p=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let p=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(p?Xr(p):null),detail:p?Xr(p):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&wI.test(gI(f.content))&&s.add(f.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}var wI,gI,pg=C(()=>{he();Zr();wI=/^Async (?:agent|task) launched/i,gI=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():""}});function Rn(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}function yI(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 mg(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:we(`read ${Rn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${ro(r)}`:`Read ${ro(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?Rn(e.path,n):null,s=e?.glob?` --glob ${Du(e.glob)}`:"",a=we(String(r),40);return{command:we(`grep ${Du(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?Rn(e.target_directory,n):null;return{command:we(`glob ${Du(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${we(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:we(`edit ${Rn(e.path,n)}`,140),description:`Edit ${ro(e.path)}`}:null;case"Write":return e?.path?{command:we(`write ${Rn(e.path,n)}`,140),description:`Write ${ro(e.path)}`}:null;case"Delete":return e?.path?{command:we(`delete ${Rn(e.path,n)}`,140),description:`Delete ${ro(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>Rn(s,n)).filter(Boolean);return{command:we(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=yI(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:we(`apply_patch ${Rn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${ro(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:we(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Ti,we,ro,Du,hg=C(()=>{Ti=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),we=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,ro=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);Du=t=>`"${String(t).replace(/"/g,'\\"')}"`});import Ts from"node:fs";import bI from"node:fs/promises";import lr from"node:path";import kI from"node:os";import xI from"node:crypto";import SI from"node:zlib";function yg(t){let e=xI.createHash("md5").update(String(t)).digest("hex");return lr.join(vI,e)}function bg(t){try{let e=JSON.parse(Ts.readFileSync(lr.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 TI(t){let e=yg(t),n;try{n=Ts.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=lr.join(e,o.name),a=lr.join(s,"store.db");if(!Ts.existsSync(a))continue;let i=bg(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 Ai(t,e){if(!t)return null;if(e){let n=lr.join(yg(t),e),r=lr.join(n,"store.db");if(Ts.existsSync(r))return{id:e,dir:n,store:r,...bg(n)||{createdAtMs:null,updatedAtMs:null}}}return TI(t)}function wg(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function AI(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=SI.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function EI(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"||!Ti.has(r.toolName))continue;let o=mg(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}async function Bu(t,e){if(!gg)return oo;let{DatabaseSync:n}=gg,r;try{r=new n(t,{readOnly:!0})}catch{return oo}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 oo;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return oo}let a=s.lastUsedModel||null,i=s.latestRootBlobId?wg(r,s.latestRootBlobId):null;if(!i)return{...oo,model:a};let c=[],l=[],u=[],d=null,f=[];for(let p of EI(i)){let m=AI(wg(r,p));if(!m)continue;let w=m.providerOptions?.cursor;if(w?.requestId&&f.push(w.requestId),w?.modelProviderMessageId&&f.push(w.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let S of m.content){let $=S?.providerOptions?.cursor?.modelName;$&&(d=$)}l.push(..._I(m.content,e).map((S,$)=>({...S,id:S.id||`${p}:${$}`}))),u.push(...RI(m.content));let b=II(m.content);if(!b)continue;let k=[...new Set(u)];u=[],c.push({id:p,text:b,tools:k.length?k:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return oo}finally{try{r.close()}catch{}}}function kg(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(Ts.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 xg(t,e,{limit:n=80}={}){let r=Ai(t,e);if(!r)return[];let{turns:o,model:s}=await Bu(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=kg(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 Sg(t,e){let n=Ai(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Bu(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 vg(t,e,{limit:n=80}={}){let r=Ai(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await Bu(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=kg(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:Tn(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Tn(u.command,u.description),ts:i+1});return l}async function Tg(t,e){let n=Ai(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await bI.stat(n.store)).mtimeMs)}catch{return null}}var vI,II,RI,gg,oo,ju=C(()=>{Zr();hg();vI=process.env.BOSTRAT_CURSOR_CHATS||lr.join(kI.homedir(),".cursor","chats");II=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(`
112
+ `).trim():"",RI=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],gg=process.getBuiltinModule?.("node:sqlite"),oo={turns:[],trailingCommands:[],model:null,requestIds:[]}});import $I from"node:fs/promises";function Hu(t){return Ag.set(t.agent,t),t}function As(t){return Ag.get(t)||OI}async function qu(t,e={}){return(await As(t.agent).readMessages(t,e)).map(Wu(t))}async function Eg(t,e={}){return(await As(t.agent).readCommands(t,e)).map(Wu(t))}async function _g(t,e={}){return(await As(t.agent).readJobs(t,e)).map(Wu(t))}var so,Lu,Fu,Uu,OI,Ag,Wu,Ig=C(()=>{he();oi();Zr();pg();ju();Qr();so=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}},Lu=class extends so{transcript(e){return!e?.cwd||!e?.sessionId?null:We(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Gl(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?tw(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?fg(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await $I.stat(n)).mtimeMs)}catch{return null}}},Fu=class extends so{async readMessages(e,n){return xg(e.cwd,e.sessionId,n)}async readCommands(e,n){return vg(e.cwd,e.sessionId,n)}async updatedAt(e){return Tg(e.cwd,e.sessionId)}},Uu=class extends so{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return ow(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return sw(e.cwd,this.sessionId(e),n)}async updatedAt(e){return aw(e.cwd,this.sessionId(e))}},OI=new so("unknown"),Ag=new Map;Hu(new Lu("claude",uu));Hu(new Fu("cursor",Ti));Hu(new Uu("codex",bs));Wu=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role})});function fr(t){let e=t.search(zu);return(e>=0?t.slice(0,e):t).trim()}function CI(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function MI(t){let e=String(t).trim();return!!(!e||dr.test(t)||zu.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function PI(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=NI(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function NI(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(f=>f.length>3&&n.includes(f)).length;if(u)return u;let d=l.slice(0,8).trim();return d.length>=4&&n.includes(d)?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 $g(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function DI(t,e=""){let n=String(t);if(!Cg.test(n))return null;let r=n.replace(/\r/g,"").split(`
113
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let d=r[u].match(ur);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!dr.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:fr(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}function jI(t,e=""){let n=String(t).replace(/\r/g,"").split(`
114
+ `),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Mg.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(ur);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&&!(BI.test(n[c])||dr.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:fr(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 LI(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
115
+ `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(ur);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}]}}function UI(t,e=""){let n=String(t);if(!Ku.test(n))return null;let r=n.replace(/\r/g,"").split(`
116
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if(FI.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let d=r[u].match(ur);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!dr.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>Ku.test(u.label)))return null;let i=s.map(u=>({label:fr(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}]}}function Ju(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(ur);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}function HI(t,e=""){let n=String(t);if(!Pg.test(n)||!Ng.test(n)||pr.test(n))return null;let r=n.replace(/\r/g,"").split(`
117
+ `),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:fr(s.label),description:""}))}]}:null}function WI(t,e=""){let n=String(t);if(!Dg.test(n)||pr.test(n))return null;let r=n.replace(/\r/g,"").split(`
118
+ `),o=Ju(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:fr(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}]}}function KI(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!qI.test(o)||Yu.test(t[r]))break;n+=o}return n}function GI(t,e=""){let n=String(t);if(!Yu.test(n)||pr.test(n))return null;let r=n.replace(/\r/g,"").split(`
119
+ `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=KI(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}function zI(t,e=""){let n=String(t);if(!Gu.test(n)||!VI.test(n)||pr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
120
+ `)].reverse().find(a=>Gu.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"}]}]}}function JI(t,e=""){let n=String(t);if(!Bg.test(n)||pr.test(n))return null;let r=n.replace(/\r/g,"").split(`
121
+ `),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:fr(s.label),description:""}))}]}:null}function YI(t,e=""){let n=String(t);if(pr.test(n))return null;let r=n.replace(/\r/g,"").split(`
122
+ `),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(jg);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(dr.test(r[l])||zu.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 XI(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Vu.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 ZI(t,e=""){let n=String(t);if(!Og.test(n)||!Vu.test(n))return null;let r=n.replace(/\r/g,"").split(`
123
+ `),o=XI(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 P=r[A].match(ur);P&&(u<0&&(u=A),l.push({line:A,cursor:!!P[1],checked:P[3]!=null&&P[3]!==" ",hasCheckbox:P[3]!=null,label:P[4].trim(),desc:""}))}if(!l.length)return null;for(let A of l)for(let P=A.line+1;P<Math.min(r.length,A.line+4)&&!(ur.test(r[P])||dr.test(r[P]));P++){let q=r[P].trim();if(!(!q||MI(r[P])||Rg.test(q))){A.desc=CI(q);break}}let d=l.filter(A=>!Rg.test(A.label));if(!d.length)return null;let f=d.some(A=>A.hasCheckbox),p=Math.max(0,d.findIndex(A=>A.cursor)),m=s>=0?s+1:0,w=[];for(let A=m;A<u;A++){let P=r[A].trim();P&&!dr.test(r[A])&&!/[☐☒]/.test(r[A])&&w.push(P)}let b=w.join(" ")||(s>=0?$g(r[s]):"")||"The agent is asking a question",k=s>=0?PI(r[s],b):null,S=k?.currentTabLabel||(s>=0?$g(r[s]):""),$=d.map(A=>({label:fr(A.label),description:A.desc})),R=$.map(A=>A.label).join("|"),O=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${S}:${b}:${R}`.slice(0,240),D=k?`claude:ask:${k.tabs.map(A=>A.label).join("|")}`.slice(0,240):O;return{provider:"claude",box:e,multiSelect:f,cursorIndex:p,promptId:O,groupId:D,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:b,header:S,multiSelect:f,options:$}]}}function ao(t,e){let n=String(e||"");return n?t==="claude"?Og.test(n)&&Vu.test(n)||Cg.test(n)||Ku.test(n)||Mg.test(n.trimEnd().split(`
124
+ `).pop()||"")?!0:pr.test(n)?!1:Pg.test(n)&&Ng.test(n)||Dg.test(n)||Yu.test(n)||Gu.test(n)||Bg.test(n)||jg.test(n.trimEnd().split(`
125
+ `).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 mr(t,e){return!t||!e?null:t.agent==="claude"?LI(e,t.name)||ZI(e,t.name)||DI(e,t.name)||UI(e,t.name)||zI(e,t.name)||GI(e,t.name)||WI(e,t.name)||HI(e,t.name)||JI(e,t.name)||YI(e,t.name)||jI(e,t.name):t.agent==="cursor"||t.agent==="codex"?Ch(t.agent,e,t.name):null}async function Xu(t){if(!t)return null;let e=await xn(t.name).catch(()=>"");return mr(t,e)}function Lg(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 Fg(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 Og,Vu,ur,Rg,dr,zu,Cg,Mg,BI,Ku,FI,pr,Pg,Ng,Dg,Yu,qI,Gu,VI,Bg,jg,Zu=C(()=>{Ut();ai();Og=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Vu=/Chat about this/i,ur=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Rg=/^(type something|chat about this|submit)\b/i,dr=/^[─—-]{6,}\s*$/,zu=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;Cg=/^\s*Switch model\?\s*$/m;Mg=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,BI=/^\s*[─—▔-]{6,}\s*$/;Ku=/Resume from summary/i,FI=/^\s*This session is\b/;pr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;Pg=/Choose the text style that looks best with your terminal/,Ng=/Let's get started\./;Dg=/Select login method:/;Yu=/Paste code here if prompted/,qI=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;Gu=/OAuth error:/,VI=/Press Enter to retry\./;Bg=/Quick safety check: Is this a project you created or one you trust/;jg=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Ei(t,e,n){if(!t||!e)return null;for(let r of QI)if(r.provider===t.agent)try{if(r.match(e,n))return r}catch{}return null}function eR(t){let e=Qu.get(t);return e||(e={sentAt:[],last:0,emitted:new Set},Qu.set(t,e)),e}function Wg(t,e,n=Date.now()){let r=eR(t);if(r.sentAt=r.sentAt.filter(s=>n-s<Hg),r.sentAt.length>=Ug)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 qg(t,e=Date.now()){let n=Qu.get(t);return n?n.sentAt.filter(r=>e-r<Hg).length>=Ug:!1}var QI,tR,Ug,Hg,Qu,td=C(()=>{QI=[{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||""}
126
+ ${String(e||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];tR=5e3,Ug=5,Hg=3600*1e3,Qu=new Map});function io(t){return typeof t=="string"?t.trim():""}var Kg=C(()=>{});import{execFile as nR}from"node:child_process";import{promisify as rR}from"node:util";async function St(t,e,{input:n}={}){let r=nd("git",["-C",t,...e],{maxBuffer:Gg});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function _i(t){if(!t)return!1;try{let{stdout:e}=await St(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Vg(t){let{stdout:e}=await St(t,["rev-parse","--show-toplevel"]);return e.trim()}async function oR(t){try{return await St(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Es(t,e="bostrat: subtask snapshot"){let{stdout:n}=await St(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>nd("git",["-C",t,...a],{maxBuffer:Gg,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 nd("rm",["-f",r])}catch{}}}async function rd(t,e){let{stdout:n}=await St(t,["status","--porcelain"]);return n.trim()?(await St(t,["add","-A"]),await St(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function od(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await St(t,["status","--porcelain"]),r=n.split(`
127
+ `).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 sd(t,e){if(!e)return 0;try{let{stdout:n}=await St(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function co(t,e,n,r){return await St(t,["worktree","add","--no-track","-b",n,e,r]),e}async function zg(t,e){try{return await St(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function Jg(t,e,n){return await St(t,["worktree","add",e,n]),e}async function Ii(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await St(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 St(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function Yg(t){await St(t,["init","-q"]),await St(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function se(t,e){try{await St(t,["worktree","remove","--force",e])}catch{try{await St(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 ad(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function id(t,e,n,r){let{stdout:o}=await St(t,ad(["diff","--shortstat",e,n],r));return sR(o)}async function cd(t,e,n,r){let{stdout:o}=await St(t,ad(["diff","--name-only",e,n],r));return o.split(`
128
+ `).map(s=>s.trim()).filter(Boolean)}async function ld(t,e,n,r){let{stdout:o}=await St(t,ad(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await ed(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await ed(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 ed(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function ed(t,e,n){try{return await St(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 St(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
129
+ `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var nd,Gg,ud=C(()=>{nd=rR(nR),Gg=64*1024*1024});import dd from"node:fs/promises";import Zg from"node:path";import iR from"node:os";function ny(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(e.getDate()-(e.getDay()+6)%7),e.getTime()}function ry(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(1),e.getTime()}function oy(t){let e=new Date(ny(t));return e.setDate(e.getDate()+7),e.getTime()}function sy(t){let e=new Date(ry(t));return e.setMonth(e.getMonth()+1),e.getTime()}function $i(t=Date.now()){for(let[e,n]of Object.entries(lo)){let r=n.startOf(t);if(r!==Ke[e]){Ke[e]=r;for(let o of hr.values())o[e]=0;for(let o of uo)o.includes(`:${e}:`)&&!o.endsWith(`:${r}`)&&uo.delete(o)}}}function ay(t,e){let n=ge?.caps?.[t]?.[lo[e].capKey];return Number.isFinite(n)&&n>0?n:null}function fo(t,e=Date.now()){if(!t||!pd||!ge?.caps)return null;$i(e);let n=hr.get(t);for(let r of Object.keys(lo)){let o=ay(t,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:t,window:r,limitUsd:o,spentUsd:Ri(s),resetsAt:lo[r].nextStart(e)}}return null}function iy(t,e){let n=`hit:${t}:${e.provider}:${e.window}:${Ke[e.window]}`;return uo.has(n)?!1:(uo.add(n),!0)}function Xg(t,e,n,r=Date.now()){if(!t||!e)return;$i(r);let o=cR(t);for(let[s,a]of Object.entries(lo))n>=Ke[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function lR(t,e,n=Date.now()){if(!ge?.caps||!fd)return;let r=hr.get(t);if(r)for(let[o,s]of Object.entries(lo)){let a=ay(t,o);if(a==null)continue;let i=r[o]||0;if(i<a*ey||i>=a)continue;let c=`warn:${t}:${o}:${Ke[o]}`;if(!uo.has(c)){uo.add(c);try{let l=await fd(e);if(!l)continue;await I(l,{type:"usage.cap.warning",actor:"system",provider:t,payload:{provider:t,window:o,limitUsd:a,spentUsd:Ri(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function cy(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 Oi(t,{source:e="local",updatedAt:n=Date.now()}={}){ge={caps:cy(t),updatedAt:n,source:e};try{await dd.mkdir(Qg,{recursive:!0}),await dd.writeFile(ty(),JSON.stringify(ge,null,2))}catch{}return _s()}function _s(t=Date.now()){$i(t);let e=new Set([...hr.keys(),...Object.keys(ge?.caps||{})]),n={};for(let r of e){let o=hr.get(r);n[r]={weeklyUsd:Ri(o?.weekly||0),monthlyUsd:Ri(o?.monthly||0)}}return{caps:ge?.caps||{},configured:!!ge,seeded:pd,updatedAt:ge?.updatedAt??null,spent:n,windows:{weekly:{start:Ke.weekly,resetsAt:oy(t)},monthly:{start:Ke.monthly,resetsAt:sy(t)}},warnPct:ey}}async function ly({listTracks:t,getTrack:e}={}){fd=e||null,$i();try{let o=JSON.parse(await dd.readFile(ty(),"utf8"));ge={caps:cy(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{ge=null}let n=Date.now();Yt.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=qe(o);Xg(o.provider||"unknown",s,o.ts??Date.now()),lR(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(Ke.weekly,Ke.monthly);for(let o of await t?.()||[]){let s;try{s=await F(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||Xg(a.provider||"unknown",qe(a).usd,i,n)}}return pd=!0,_s()}var Qg,ty,ey,lo,ge,pd,Ke,hr,uo,fd,cR,Ri,Ci=C(()=>{ct();cr();Qg=process.env.BOSTRAT_STATE_DIR||Zg.join(process.env.HOME||iR.tmpdir(),".bostrat"),ty=()=>Zg.join(Qg,"usage-caps.json"),ey=.8;lo={weekly:{startOf:ny,nextStart:oy,capKey:"weeklyUsd"},monthly:{startOf:ry,nextStart:sy,capKey:"monthlyUsd"}},ge=null,pd=!1,Ke={weekly:0,monthly:0},hr=new Map,uo=new Set,fd=null,cR=t=>{let e=hr.get(t);return e||(e={weekly:0,monthly:0},hr.set(t,e)),e};Ri=t=>Math.round(t*1e4)/1e4});import uR from"node:fs/promises";import dR from"node:os";import uy from"node:path";function gy(t){return t==="codex"?hR:t==="cursor"?wy:my}function dy(t){let e=String(t||"").split(`
130
+ `),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 fy(t){let e=String(t||"").split(`
131
+ `),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(`
132
+ `);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 py(t){let e=String(t||"").split(`
133
+ `),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 yy(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&&!hy.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=wy.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 Is(t,e,{timeoutMs:n=fR}={}){let r=Date.now()+n;for(;;){let o=await bt(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 Mt(t,`/model ${e.model}`,_e("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function yR(t,e){await ti(t),await Mt(t,"/model",{enter:!1}),await Mi(250),await vt(t,["Enter"],{kind:"model-switch"});let n=await Is(t,dy);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await vt(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 Mt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await Is(t,i=>fy(i)||!dy(i)&&o(i)&&{done:o(i)});if(!s)throw await vt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?hy.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 vt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await Mt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Is(t,i=>!fy(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=uy.join(process.env.CURSOR_CONFIG_DIR||uy.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 ti(t),await ni(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await Is(t,py),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 Is(t,py,{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 vt(t,["Down"],{kind:"model-switch"}),await Mi(250)}if(!n||!s||!o(s)){await vt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await vt(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 vt(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 by(t,e,n){return t==="codex"?yR(e,n):t==="cursor"?kR(e,n):gR(e,n)}var Mi,fR,pR,my,mR,hR,hy,wy,ky=C(()=>{Ut();ms();Mi=t=>new Promise(e=>setTimeout(e,t)),fR=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,pR=300,my=[{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([...my.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"}],hy=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),wy=[{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 Ni(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:te(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:te(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:te(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:te(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:te(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:te(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:te(t.agent),payload:{...po(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:te(t.agent||t.toAgent),payload:{kind:"subtask-failed",...po(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:te(t.agent),payload:{change:"subtask-resumed",...po(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",...po(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...po(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:te(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...po(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:te(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}function vy(t,e){let n=Ni(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;hd(()=>I(r,n))}async function Ty(t){if(!(!t?.id||xy.has(t.id))){xy.add(t.id);try{let e=await F(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=Ni(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(t,o)}}catch{}}}function po(t){let{type:e,at:n,...r}=t;return r}function Ay(t,e,n){!n?.promptId||md.get(t.id)===n.promptId||(md.set(t.id,n.promptId),hd(()=>I(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:te(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 wd(t,e,n,r={}){md.delete(t.id),hd(()=>I(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:te(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}var te,Pi,hd,Sy,xy,md,gd=C(()=>{ct();te=t=>t||null;Pi=Promise.resolve(),hd=t=>(Pi=Pi.then(t).catch(()=>{}),Pi),Sy=()=>Pi;xy=new Set;md=new Map});function ER(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 ER(n).reverse()){if(!SR.test(r))continue;let o=r.endsWith("?")&&r.length<=AR&&TR.test(r);if(vR.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function Cy(t){let e=String(t||""),n=[];_R.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 mo(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 d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let f=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(f+=` ${d.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(f+=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."),f}function My(t,{branch:e=null,base:n=null,prNumber:r=null,offeredRebase:o=!1}={}){let s=new Set(Array.isArray(t)?t:["commit","push","pr"]),a=e?` on this branch (${e})`:"",i=[];s.has("commit")&&i.push(`commit the work${a} with a clear commit message`),s.has("rebase")&&i.push(`rebase ${s.has("commit")?"the branch":`this branch${e?` (${e})`:""}`} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`),i.push("move the work to a fresh branch (branch off the current HEAD with a clear new name)"),i.push("push the new branch to the remote"),i.push(`open a pull request${n?` against ${n}`:""} from the new branch with a concise title and a description of what changed and why (use \`gh pr create\`)`);let c=[];s.has("commit")||c.push("don't create any new commits \u2014 ship what's already committed"),o&&!s.has("rebase")&&c.push("don't rebase \u2014 leave the branch's history as it is");let l=`Yes \u2014 go ahead, as a NEW pull request: ${i.join(", then ")}.`;return c.length&&(l+=` ${c.join(", and ").replace(/^./,u=>u.toUpperCase())}.`),l+=` Leave the existing open PR${r?` #${r}`:""}${e?` and its remote branch (${e})`:" and its remote branch"} exactly as they are \u2014 don't push ${e||"the old branch"} itself.`,l}function Py(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}if(o.type==="pr.exists.answered"&&s.choice==="new"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"||o.type==="pr.exists.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<=CR&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Ny(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+MR||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Dy(t){let e=(Array.isArray(t)?t:[]).filter(r=>Ey[r]).map(r=>Ey[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.`}var _y,Iy,Ry,$y,SR,vR,TR,AR,Di,_R,IR,RR,$R,OR,CR,MR,Ey,ho=C(()=>{_y=/(?<!\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,SR=new RegExp(`\\b(?:${_y}|${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,TR=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,AR=90;Di=["commit","rebase","push","pr"],_R=new RegExp(`\\b(?:${_y})\\b`,"i"),IR=new RegExp(`\\b(?:${$y})\\b`,"i"),RR=new RegExp(`\\b(?:${Iy})\\b`,"i"),$R=new RegExp(`\\b(?:${Ry})\\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;CR=3e4;MR=2500;Ey={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import PR from"node:fs";import yd from"node:fs/promises";import Fy from"node:path";import NR from"node:os";function Wy(t,e=!1){if(!e&&t-jy<DR)return;jy=t;let n=JSON.stringify({windowStart:wo,byCaller:Object.fromEntries(Ge),lastHour:$n});Ly=Ly.then(()=>yd.mkdir(Uy,{recursive:!0})).then(()=>yd.writeFile(`${Bi}.tmp`,n)).then(()=>yd.rename(`${Bi}.tmp`,Bi)).catch(()=>{})}function qy(t){t-wo<Hy||($n={start:wo,total:ji(Ge),byCaller:Object.fromEntries(Ge)},$n.total&&console.log(`[github] ${$n.total} calls last hour`,$n.byCaller),wo=t,Ge=new Map,Wy(t,!0))}function Ve(t="other",e=Date.now()){qy(e),Ge.set(t,(Ge.get(t)||0)+1),Wy(e)}function Ky(t=Date.now()){return qy(t),{windowStart:wo,total:ji(Ge),byCaller:Object.fromEntries(Ge),lastHour:$n,backoffUntil:wr>t?wr:null}}function On(t){return BR.test(String(t?.message||t||""))}function Cn(t,e=Date.now()){return wr<=e&&console.warn(`[github] rate limited \u2014 backing off ${By/6e4} min:`,String(t?.message||t||"").slice(0,200)),wr=Math.max(wr,e+By),wr}function Li(t=Date.now()){return Math.max(0,wr-t)}var Uy,Bi,Hy,By,DR,wo,Ge,$n,wr,jy,ji,Ly,BR,Rs=C(()=>{Uy=process.env.BOSTRAT_STATE_DIR||Fy.join(process.env.HOME||NR.tmpdir(),".bostrat"),Bi=Fy.join(Uy,"github-metrics.json"),Hy=60*6e4,By=5*6e4,DR=6e4,wo=Date.now(),Ge=new Map,$n=null,wr=0,jy=0,ji=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(PR.readFileSync(Bi,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Hy?(wo=t.windowStart,Ge=new Map(Object.entries(t.byCaller||{})),$n=t.lastHour||null):ji(new Map(Object.entries(t.byCaller||{})))&&($n={start:t.windowStart,total:ji(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}Ly=Promise.resolve();BR=/rate limit|too quickly|abuse detection|HTTP 429/i});import jR from"node:crypto";import Fi from"node:fs/promises";import Gy from"node:path";import LR from"node:os";async function At(){return Mn||(bd??=Fi.readFile(xd,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(Mn=t,!e&&gt()){let{records:n,source:r}=await Ot("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;Mn[V(i.repo,i.number)]=i}Object.keys(Mn).length&&await Sd().catch(()=>{})}}return Mn}).finally(()=>bd=null),bd)}async function Sd(){let t=JSON.stringify({links:Mn},null,2),e=`${xd}.${process.pid}.tmp`;await Fi.mkdir(Vy,{recursive:!0}),await Fi.writeFile(e,t),await Fi.rename(e,xd)}async function Pt(t){kd=kd.then(Sd,Sd),await kd.catch(()=>{});let e=Mn?.[t];e&&Ft("prlinks",{id:zy(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}async function ee({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 At(),c=V(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:FR.has(a),firstSeen:Date.now()},await Pt(V(t,e)),{link:i[c],created:!0})}async function $s(t,e){if(!t||e==null)return null;let n=(await At())[V(t,e)];return!n||n.announced?null:(n.announced=!0,await Pt(V(t,e)),n)}async function Ht(t,e){return!t||e==null?null:(await At())[V(t,e)]??null}async function Pn(t,e,n){if(!t||e==null)return null;let r=(await At())[V(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 Pt(V(t,e)),r):null}async function Jy(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await At())[V(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Pt(V(t,e)),o):null}async function Yy(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await At())[V(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Pt(V(t,e)),o):null}async function Ui(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await At())[V(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Pt(V(t,e)),r)}async function Hi(t,e){if(!t||e==null)return null;let n=(await At())[V(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Pt(V(t,e)),n)}async function Wi(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await At())[V(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Pt(V(t,e)),r)}async function qi(t,e){if(!t||e==null)return null;let n=(await At())[V(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Pt(V(t,e)),n)}async function Xy(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await At())[V(t,e)];return r?(r.baseFoldAt=n,await Pt(V(t,e)),r):null}async function Zy(t,e){if(!t||e==null)return null;let n=(await At())[V(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Pt(V(t,e)),n)}async function Qy(t,e,n){if(!t||e==null||!n)return null;let r=(await At())[V(t,e)];return r?(r.lastHeadSha=n,await Pt(V(t,e)),r):null}async function Os(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await At())[V(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Pt(V(t,e)),r):null}async function gr(t,e,n=!0){if(!t||e==null)return null;let r=(await At())[V(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Pt(V(t,e)),r):null}async function Cs(t,e,n=!0){if(!t||e==null)return null;let r=(await At())[V(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Pt(V(t,e)),r):null}async function tb(t,e,n){if(!t||e==null)return null;let r=(await At())[V(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 Pt(V(t,e)),r}async function Ms(t,e){if(!t||e==null)return null;let n=(await At())[V(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 Pt(V(t,e)),n)}async function eb(){return Object.values(await At()).filter(t=>!UR.has(t.state))}async function yr(){return Object.values(await At()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function nb(){if(!gt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await At(),r=0;for(let[o,s]of Object.entries(n))await Ft("prlinks",{id:zy(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Vy,xd,V,zy,Mn,bd,kd,FR,UR,Nn=C(()=>{yn();Vy=process.env.BOSTRAT_STATE_DIR||Gy.join(process.env.HOME||LR.tmpdir(),".bostrat"),xd=Gy.join(Vy,"pr-links.json"),V=(t,e)=>`${t}#${Number(e)}`,zy=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=jR.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},Mn=null,bd=null,kd=Promise.resolve();FR=new Set(["api","observed","reconciled"]);UR=new Set(["merged","closed","lost"])});function Ki(t,e,n){let{state:r,reason:o}=Zl(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of Ps)a-c.at>=rb()&&Ps.delete(i);Ps.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=Ps.get(t);return s?Date.now()-s.at>=rb()?(Ps.delete(t),!1):s.busy:!1}async function vd(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await bt(t,{lines:Ct}).catch(()=>"");if(!Ki(t,e,r))return Dh(e,r)?{working:!0,prompted:!1}:n;if(ao(e,r)){let o=await xn(t).catch(()=>r),s=mr({name:t,agent:e},o);if(s)return!Ei({name:t,agent:e},s,o)||qg(t)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function Gi(t,e){return(await vd(t,e)).working}var rb,Ps,Vi=C(()=>{Ut();di();Zu();td();rb=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,Ps=new Map});import{execFile as HR}from"node:child_process";import{promisify as WR}from"node:util";async function Td(t,e,{midTurn:n=Gi,pollMs:r=qR,capMs:o=KR}={}){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}function t$(t,e){let n=String(t||"").match(QR);return!!n&&n[1]!==e}function Ad(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function ib(t){let e=String(t||""),n=e.match(JR);if(!n)return null;let r=Ad(e,n.index);return XR.test(r)?null:{repo:r.match(ab)?.[1]??null,head:r.match(ZR)?.[1]??null}}function cb(t){let e=String(t||""),n=e.match(YR);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("-")){e$.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(ab)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function r$(t,e,{exclude:n=()=>!1}={}){let r=async i=>{Ve("observer");let{stdout:c}=await ob("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",n$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw On(l)&&Cn(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=e-GR&&!n(c)),s=await Te({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 o$(t,e){if(e)return[e];let n=[];for(let r of lb(t)){let o=await rt(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Fe().catch(()=>[])){let o=await rt(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function lb(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 s$(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)||t$(c.headRefName,t.id),i=[];for(let c of await o$(t,e?.repo??null)){let l;try{l=await r$(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await Ht(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===t.id&&d.announced)return{settled:!0};if(d&&d.trackId!==t.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==t.id||d.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}function ub(t,e,{delays:n=sb,turnEnd:r}={}){let o=ib(e.command);if(!o)return null;let s=!!cb(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(Dn.has(a))return null;Dn.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await s$(t,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:f}=u,{link:p,created:m}=await ee({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await $s(d,f.number))?null:(await Td(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:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{Dn.delete(a)}})();return c.catch(()=>Dn.delete(a)),c}function db(t,e,{delays:n=sb,turnEnd:r,openedWait:o}={}){let s=cb(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(Dn.has(a))return null;Dn.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 d=[];if(c)d=[c];else{let m=lb(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let b=await rt(w).catch(()=>null);b&&!d.includes(b)&&d.push(b)}}if(l||=t.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(m,w)=>{let b=await Ht(c,m.number);return b&&b.trackId!==t.id?null:(b||await ee({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Os(c,m.number),w&&((await F(t).catch(()=>[])).some($=>$.type==="pr.state.changed"&&($.payload?.change==="auto-armed"||$.payload?.change==="merged")&&$.payload?.number===m.number)||(await Td(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 w=[];for(let O of d)try{Ve("observer");let{stdout:D}=await ob("gh",["pr","view",String(l),"--repo",O,"--json",a$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Te({slug:O})}}),A=JSON.parse(D||"null");A?.number&&w.push({slug:O,pr:A})}catch(D){On(D)&&Cn(D)}let b=w.find(O=>O.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!b)continue;c=b.slug;let k=b.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return f(k,!0);continue}let S=await Ht(c,k.number);if(S&&S.trackId!==t.id)return null;S||await ee({repo:c,number:k.number,trackId:t.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Td(e.box,e.agent,r);let $=await F(t).catch(()=>[]);if($.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="merged"&&O.payload?.number===k.number))return null;let R=O=>O.some(D=>D.type==="pr.state.changed"&&D.payload?.change==="opened"&&D.payload?.number===k.number),J=0;if(!R($)){if(ib(e.command)&&await $s(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 Ht(c,k.number))?.announced){let O=o?.capMs??VR,D=o?.pollMs??zR,A=Date.now()+O;for(;Date.now()<A&&(await new Promise(P=>setTimeout(P,D)),!R(await F(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 Je(c,k.number).catch(()=>{}),await ze(c,k.number).catch(()=>{}),await Pn(c,k.number,"merged"),k}return p?f(p,!1):null}finally{Dn.delete(a)}})();return i.catch(()=>Dn.delete(a)),i}var ob,sb,qR,KR,GR,VR,zR,JR,YR,XR,ab,ZR,QR,e$,n$,Dn,a$,fb=C(()=>{ct();Zn();oe();Rs();Nn();br();Vi();ob=WR(HR),sb=[4e3,15e3,45e3],qR=2500,KR=5*6e4;GR=15e3,VR=9e4,zR=2e3,JR=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,YR=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,XR=/\s--web\b/,ab=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,ZR=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,QR=/^track\/.+-([0-9a-f]{8})$/;e$=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);n$="number,url,title,headRefName,createdAt";Dn=new Set;a$="number,url,title,headRefName,state"});import i$ from"node:fs/promises";import _d from"node:path";import c$ from"node:os";function Ed(t,e){let n=new RegExp(`\\b${e}="([^"]*)"`).exec(t);return n?n[1]:null}function zi(t){let e=l$.exec(String(t||""));if(!e)return null;let[,n,r]=e,o=Ed(n,"from"),s=o?o.replace(/^uds:/,""):null,a=pb(s);return{socket:s,pid:a,name:Ed(n,"from-name"),mode:Ed(n,"from-mode"),text:r.trim()}}function pb(t){let e=/(\d+)\.sock$/.exec(String(t||""));return e?Number(e[1]):null}function u$(){return process.env.CLAUDE_CONFIG_DIR?_d.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):_d.join(c$.homedir(),".claude","sessions")}async function d$(t){let e=pb(t);if(!e)return null;try{let n=await i$.readFile(_d.join(u$(),`${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 mb(t,e){let n=await d$(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}}var l$,Id=C(()=>{l$=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});import{randomUUID as f$}from"node:crypto";function Ns(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function hb(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function wb(t){return`job|${t.box}|id:${t.id}`}async function $d(t){return Rd.has(t.id)?Rd.get(t.id):(Ji.has(t.id)||Ji.set(t.id,F(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(Ns({role:o,sourceId:r.payload.sourceId})),n.keys.add(Ns({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(Ns({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Ns({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(hb({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(wb({id:r.payload.id,box:r.boxId}));return Rd.set(t.id,n),n}).finally(()=>Ji.delete(t.id))),Ji.get(t.id))}async function gb(t,e){try{let n=(await $d(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Ns(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?zi(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 mb(s,Ds),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}=Cy(a),l;if(t.workspace?.kind==="multi"){let u=await go(t).catch(()=>[]);l=u.length?u.map(d=>({name:d.name,slug:d.repo??null})):void 0}await I(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:f$(),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 yb(t,e){try{let n=await $d(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=hb(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}}),ub(t,o),db(t,o))}}catch{}}async function bb(t,e){try{let n=await $d(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=wb(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{}}var Rd,Ji,kb=C(()=>{ct();ho();fb();Id();br();Rd=new Map,Ji=new Map});function p$(t){let e=[],n=null;for(let r of String(t||"").split(`
134
+ `)){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}async function m$(t,e){let n=`${t.id}:${e.name}`;if(!Od.has(n)){let r=await F(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);Od.set(n,o)}return Od.get(n)}async function xb(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await ar(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await m$(t,e),o=p$(await Jt(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:_n(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{}}var Od,Sb=C(()=>{ct();Qr();he();vs();Od=new Map});import Yi from"node:fs";import Md from"node:path";import h$ from"node:os";async function y$({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(g$,{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:vb})});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<vb)break}return r}function b$(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}}function Ab(){try{return JSON.parse(Yi.readFileSync(Xi(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function x$(t){try{Yi.mkdirSync(Md.dirname(Xi()),{recursive:!0});let e=Xi()+".tmp";Yi.writeFileSync(e,JSON.stringify(t)),Yi.renameSync(e,Xi())}catch{}}async function v$({fetchImpl:t,now:e=Date.now()}={}){if(Bn.rows&&e-Bn.at<k$)return Bn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Ab(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await y$({sinceMs:o,cookie:n,fetchImpl:t});Bn={at:e,rows:s.map(b$).filter(Boolean),error:null}}catch(s){Bn={at:e,rows:null,error:String(s?.message||s)}}return Bn.rows}async function T$(t,e){let n=`${t.id}:${e.name}`;if(!Cd.has(n)){let r=await F(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);Cd.set(n,o)}return Cd.get(n)}async function Eb(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Tb())return;let r=await v$({fetchImpl:n});if(!r||!r.length)return;let o=await Sg(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await T$(t,e),a=Ab(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${t.id}:${e.name}:${l.id}`))continue;s.add(l.id),i.add(`${t.id}:${e.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=_n(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await I(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}x$({sinceMs:c,seen:[...i].slice(-S$)})}catch{}}var w$,Tb,g$,vb,k$,Bn,Xi,S$,Pd,Cd,Nd=C(()=>{ct();ju();vs();w$=process.env.BOSTRAT_STATE_DIR||Md.join(process.env.HOME||h$.tmpdir(),".bostrat"),Tb=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,g$="https://cursor.com/api/dashboard/get-filtered-usage-events",vb=100;k$=5*6e4,Bn={at:0,rows:null,error:null},Xi=()=>Md.join(w$,"cursor-usage.json");S$=5e3;Pd=()=>({enabled:Tb(),lastPollAt:Bn.at||null,error:Bn.error}),Cd=new Map});import Ls from"node:fs/promises";import Xt from"node:path";import A$ from"node:os";import E$ from"node:net";import{randomUUID as _$}from"node:crypto";import{execFile as I$}from"node:child_process";import{promisify as R$}from"node:util";function D$(t){for(let e of Hd(t))if(!Cb.test(e))return e;return null}function Mb(t){let e=Hd(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function B$(t,e){let n=Mb(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Xt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Xt.resolve(t,n[r+1])}return t}function L$(t){let e=!1,n=!1;for(let r of Hd(t)){if(!e){if(Cb.test(r))continue;e=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Dd(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Dd(r))return`serves the filesystem root ("${r}")`;continue}if(Dd(r))return`serves the filesystem root ("${r}")`;if(j$(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function U$(t,e){if(Xt.resolve(t)===Xt.resolve(e))return!0;let[n,r]=await Promise.all([Zi(t),Zi(e)]);return n===r}async function H$(t){let n=(await Ls.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 Pb(Xt.join(t,o.name,Ld))?`./${o.name}`:null))).filter(Boolean)}async function W$(t,e){let[n,r]=await Promise.all([Zi(t),Zi(e)]),o=Xt.relative(n,r);if(o&&(o.startsWith("..")||Xt.isAbsolute(o)))throw Bs(`serve directory escapes the Track workspace: ${e}`)}async function q$(t,e){let n=L$(e);if(n)throw Bs(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await F$(t))throw Bs(`serve directory does not exist: ${t}`);let r=D$(e);if(!r)return;if(!r.includes("/")&&!await ch(r,{loginShell:!0,failOpen:!0}))throw Bs(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(M$.test(e)||P$.has(r)||!C$.has(r)||!Mb(e).includes("run")||N$.test(e))return;let o=B$(t,e);if(await Pb(Xt.join(o,Ld)))return;let s=await H$(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Bs(`no ${Ld} in ${o}${a}`)}async function Ze(){return Re||(Bd||=(async()=>{try{Re=JSON.parse(await Ls.readFile(Ob,"utf8"))}catch{Re={views:[],adoptWatermarks:{}}}finally{Bd=null}return Re.views||=[],Re.adoptWatermarks||={},Re})(),Bd)}async function Oe(){await zt(Ob,Re)}function Xe(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,cmd:t.cmd||null,...n}}).catch(()=>{})}function qd(t){return js.get(t)||null}function Db(t,{name:e,box:n}={}){let r=js.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 js.set(t,o),o}function Fd(t,e){let n=js.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Qi(t,{error:e=null}={}){let n=js.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Bb(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 Ud(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function K$(t,e){let n=Ud(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Rb.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Rb.has(s))return s}}function Fs(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=E$.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 Kd(t){if(process.platform!=="darwin"){await $e("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await $e("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 jb(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function G$(t,e){let n=new Set(t.views.map(o=>o.port)),r=jb();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=_b;o<=Ib;o++)if(!(n.has(o)||r.has(o))&&!await Fs(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${_b}-${Ib}`)}async function V$(t){try{let{stdout:e}=await $e(N,B("list-sessions","-F","#{session_name}"));return e.split(`
135
+ `).some(n=>n.trim()===yo(t))}catch{return!1}}async function Us(t){try{await $e(N,B("kill-session","-t",yo(t)))}catch{}}async function Gd(t){if(!await ls())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=yo(t.slug),n=t.serveCwd??t.cwd,r=Xt.dirname(process.execPath);await $e(N,B("new-session","-d","-s",e,"-c",n)),await $e(N,B("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await $e(N,B("send-keys","-t",e,"Enter")),await $e(N,B("send-keys","-t",e,"-l",t.cmd)),await $e(N,B("send-keys","-t",e,"Enter"))}async function $b(t,e=12){try{let{stdout:n}=await $e(N,B("capture-pane","-t",yo(t),"-p"));return String(n||"").trimEnd().split(`
115
136
  `).filter(Boolean).slice(-e).join(`
116
- `)}catch{return""}}var je=new Map,dE=12e4;async function fE(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await uE(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 Zo(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>dE?(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 Dl=!1;async function Ha(){if(!Dl){Dl=!0;try{let t=await Fe(),e=!1;for(let n of t.views)await fE(n)&&(e=!0);e&&await Te()}finally{Dl=!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 ql(t){return ve&&ve.views.find(e=>e.slug===t)||null}async function Kl({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 aE(r,u),await iE(u,n)},d=await Fe();if(i!=="detected"){let h=Ll(e),y=String(n).trim(),k=d.views.find(v=>v.trackId===t&&(v.slug===h||Ll(v.name)===h||v.cmd===y&&Wt.resolve(v.serveCwd??v.cwd)===u));if(k)return pE(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:K_().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:cE(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await lE(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Wl(m),d.views.push(m),await Te(),await Le(m,"created",{origin:i}),jl(t,m.id),i==="detected"&&await Zo(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await Te(),await Le(m,"live")),{...m}}async function pE(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 oE(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Zo(e.port))return await Te(),jl(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 Hl(e.port),e.origin="agent"):await Qo(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 Wl(e),await Te(),await Le(e,"created",{restarted:!0,reused:!0}),jl(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 Hl(n.port):await Qo(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 Qo(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),je.delete(n.id),await Wl(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 Hl(r.port):await Qo(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 Qo(r.slug),je.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await Te()),n.length}var mE=/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(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(!Vh().has(c)&&!n.views.some(l=>l.port===c)&&await Zo(c))try{i.push(await Kl({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 hE from"node:os";import{randomUUID as wE}from"node:crypto";var Yn=process.env.BOSTRAT_STATE_DIR||qe.join(process.env.HOME||hE.tmpdir(),".bostrat"),ew=qe.join(Yn,"runbooks.json"),qa=qe.join(Yn,"runbooks"),tw=256*1024,gE=[{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 ts(t){return gE.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Ue=null,Gl=null;async function Ke(){return Ue||(Gl||=(async()=>{let t=!0;try{Ue=JSON.parse(await We.readFile(ew,"utf8"))}catch{Ue={runbooks:[]},t=!1}finally{Gl=null}return Ue.runbooks||=[],!t&&ht()&&await yE().catch(()=>{}),Ue})(),Gl)}async function Zn(){await Ft(ew,Ue)}var es=t=>Promise.resolve(t).catch(()=>{});function Ka(t){return es(Pt("runbooks",{...t},{trackId:t.trackId}))}async function yE(){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=ts(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 bE(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 Vl(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:wE().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),es(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");bE(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),es(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"),es(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(()=>{});es(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 os from"node:fs/promises";import Tw from"node:path";import JE from"node:os";import{randomUUID as Aw}from"node:crypto";import kE from"node:fs/promises";import dw from"node:path";import xE from"node:os";var fw=()=>dw.join(process.env.BOSTRAT_STATE_DIR||dw.join(process.env.HOME||xE.tmpdir(),".bostrat"),"integrations.json");async function Ge(){try{return JSON.parse(await kE.readFile(fw(),"utf8"))||{}}catch{return{}}}var Va=["linked","prOpened","prMerged","create"],zl=["prompt","auto","off"],vE={create:["prompt","off"]},pw=t=>vE[t]??zl;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 Jl(){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<zl.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${zl.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 ns(){let t=(await Ge()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function SE(){let t=(await Ge()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var TE={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Xa(){let e=(await Ge()).voiceChat||{},n=e.provider||"openai",r=TE[n];if(!r)return null;let o=n==="openai"?await SE():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 Yl={};Ef(Yl,{createComment:()=>CE,findIssueUrl:()=>IE,issueStates:()=>DE,mdToAdf:()=>yw,moveIssue:()=>BE,parseIssueRef:()=>EE,resolveIssue:()=>RE,uploadImage:()=>NE,upsertCard:()=>$E,whoami:()=>Xl});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 AE=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,_E=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function EE(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(AE);return r?r[1].toUpperCase():null}let n=e.match(_E);return n?n[1]:null}function IE(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 RE(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 $E(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 OE=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function ME(t){let e=[],n=0;for(let r of t.matchAll(OE)){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(...ME(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 CE(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 Xl(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 NE(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 PE={new:"unstarted",indeterminate:"started",done:"completed"},ww=t=>PE[t?.key]??"started";async function DE(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 BE(t,e,n){await er(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var eu={};Ef(eu,{createComment:()=>KE,createIssue:()=>tu,createProject:()=>Ql,findIssueUrl:()=>HE,issueStates:()=>VE,moveIssue:()=>zE,parseIssueRef:()=>UE,resolveIssue:()=>WE,teams:()=>Ur,uploadImage:()=>GE,upsertCard:()=>qE,whoami:()=>Zl});var jE=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Ae(t,e,n={}){let r=await fetch(jE(),{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 LE=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,FE=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function UE(t){let e=String(t||"").trim(),n=e.match(LE);if(n)return n[1].toUpperCase();let r=e.match(FE);return r?r[1]:null}function HE(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 WE(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 qE(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 KE(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 Zl(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 GE(t,{filename:e,contentType:n,buffer:r}){let o=await Ae(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
118
- fileUpload(contentType: $contentType, filename: $filename, size: $size) {
119
- success
120
- uploadFile { uploadUrl assetUrl headers { key value } }
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 VE(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 zE(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 Ql(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 tu(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:eu,config:Qn},jira:{api:Yl,config:Ja}};async function cu(){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 lu(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||JE.tmpdir(),".bostrat"),su=()=>Tw.join(Ew(),"tickets.json"),Iw=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Ya=t=>`${Iw()}/track/${t}`,bn=null,nu=null,ru=Promise.resolve();async function as(){return bn||(nu??=os.readFile(su(),"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 au().catch(()=>{})}}return bn}).finally(()=>nu=null),nu)}async function au(){let t=JSON.stringify({tickets:bn},null,2),e=`${su()}.${process.pid}.tmp`;await os.mkdir(Ew(),{recursive:!0}),await os.writeFile(e,t),await os.rename(e,su())}async function kn(t=null){if(ru=ru.then(au,au),await ru.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 as())[t]??null}async function Za(){return{...await as()}}async function Rw(){return(await cu()).length>0}async function Hr(t,e,{source:n="api"}={}){let r=await cu();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 as())[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 ss(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 rs(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Mw(t.id,c,"linked"),c}async function uu(t){let e=await as();return e[t]?(delete e[t],await kn(t),!0):!1}var iu=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 XE(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${iu(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${iu(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function YE(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(iu(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function ss(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:YE(e,r)})}function Qa(t){let e=new URL(Iw());return`${e.protocol}//${t}.${e.host}/`}var ZE={linked:"linked",opened:"prOpened",merged:"prMerged"};function QE(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?.[ZE[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=lu(s,a),c=QE(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=lu(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 du(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=lu(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 du(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 tI(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 eI(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=tI(e),o=await Ur(n),s=await eI(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 Ql(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 tu(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 rs(t,e){let n=await nr(t);n&&await n.api.createComment(n.cfg,t,e)}async function nI(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 os.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),ou=new Map;function rI(t,e){if(xw()<=0)return ss(t,e);if(ou.has(t))return;let n=setTimeout(()=>{ou.delete(t),_e(t).then(r=>r?ss(t,r):null).catch(()=>{})},xw());n.unref?.(),ou.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 oI(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 cu())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 ss(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 ss(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await $w(n,t.trackId).catch(()=>null):null;await rs(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: ${XE(o)}.`:"")),await Mw(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await nI(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await rs(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 rs(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 rs(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 rI(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=>{oI(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 as(),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 sI from"node:crypto";import ti from"node:fs/promises";import Uw from"node:path";import aI from"node:os";var Hw=process.env.BOSTRAT_STATE_DIR||Uw.join(process.env.HOME||aI.tmpdir(),".bostrat"),mu=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=sI.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},xn=null,fu=null,pu=Promise.resolve();async function xt(){return xn||(fu??=ti.readFile(mu,"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 hu().catch(()=>{})}}return xn}).finally(()=>fu=null),fu)}async function hu(){let t=JSON.stringify({links:xn},null,2),e=`${mu}.${process.pid}.tmp`;await ti.mkdir(Hw,{recursive:!0}),await ti.writeFile(e,t),await ti.rename(e,mu)}async function It(t){pu=pu.then(hu,hu),await pu.catch(()=>{});let e=xn?.[t];e&&Pt("prlinks",{id:Ww(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var iI=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:iI.has(a),firstSeen:Date.now()},await It(K(t,e)),{link:i[c],created:!0})}async function is(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 cs(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 ls(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 us(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 cI=new Set(["merged","closed","lost"]);async function Xw(){return Object.values(await xt()).filter(t=>!cI.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 lI(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 lI(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 wu=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function uI(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 gu(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(wu(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(wu(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(`${wu(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)?uI(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function dI(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:dI(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 fI}from"node:child_process";import{promisify as pI}from"node:util";import{randomUUID as mI}from"node:crypto";var Wr=pI(fI),ai=t=>new Promise(e=>setTimeout(e,t)),hI=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,og=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,wI=250,gI=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 yu(t,e){let n=Date.now()+e;for(;;){if(await Er(t))return!0;if(Date.now()>n)return!1;await ai(wI)}}async function yI(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 bI(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 bu(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)||hI;await q({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Er(t)&&(await yI(t,o),!await yu(t,a)&&(i=!0,await q({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await ag(t),!await yu(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 bI(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 yu(t,og)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||mI(),m=d?n:Wc(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(gI),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 NI from"node:fs/promises";var kI=/^Async (?:agent|task) launched/i,xI=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&&kI.test(xI(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 ds from"node:fs";import SI from"node:fs/promises";import or from"node:path";import TI from"node:os";import AI from"node:crypto";import _I 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 ku=t=>`"${String(t).replace(/"/g,'\\"')}"`;function vI(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 ${ku(e.glob)}`:"",a=ie(String(r),40);return{command:ie(`grep ${ku(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 ${ku(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=vI(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 EI=process.env.BOSTRAT_CURSOR_CHATS||or.join(TI.homedir(),".cursor","chats");function dg(t){let e=AI.createHash("md5").update(String(t)).digest("hex");return or.join(EI,e)}function fg(t){try{let e=JSON.parse(ds.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 II(t){let e=dg(t),n;try{n=ds.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(!ds.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(ds.existsSync(r))return{id:e,dir:n,store:r,...fg(n)||{createdAtMs:null,updatedAtMs:null}}}return II(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 RI(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=_I.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function $I(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 OI(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 MI=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():"",CI=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 xu(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 $I(i)){let m=RI(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(...OI(m.content,e).map((v,I)=>({...v,id:v.id||`${p}:${I}`}))),u.push(...CI(m.content));let y=MI(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(ds.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 xu(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 xu(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 xu(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 SI.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}},vu=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?Yc(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 NI.stat(n)).mtimeMs)}catch{return null}}},Su=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)}},Tu=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))}},PI=new Gr("unknown"),yg=new Map;function Au(t){return yg.set(t.agent,t),t}function fs(t){return yg.get(t)||PI}Au(new vu("claude",fl));Au(new Su("cursor",ii));Au(new Tu("codex",qo));var _u=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function Eu(t,e={}){return(await fs(t.agent).readMessages(t,e)).map(_u(t))}async function bg(t,e={}){return(await fs(t.agent).readCommands(t,e)).map(_u(t))}async function kg(t,e={}){return(await fs(t.agent).readJobs(t,e)).map(_u(t))}var Sg=/to navigate\b.*\bEsc to cancel|Enter to select\b/,$u=/Chat about this/i,sr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,xg=/^(type something|chat about this|submit)\b/i,ar=/^[─—-]{6,}\s*$/,Ou=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function ir(t){let e=t.search(Ou);return(e>=0?t.slice(0,e):t).trim()}function DI(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function BI(t){let e=String(t).trim();return!!(!e||ar.test(t)||Ou.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function jI(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=LI(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function LI(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 FI(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*$/,UI=/^\s*[─—▔-]{6,}\s*$/;function HI(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&&!(UI.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 WI(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 Iu=/Resume from summary/i,qI=/^\s*This session is\b/;function KI(t,e=""){let n=String(t);if(!Iu.test(n))return null;let r=n.replace(/\r/g,"").split(`
133
- `),o=-1;for(let u=r.length-1;u>=0;u--)if(qI.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=>Iu.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 Mu(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 GI(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=Mu(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 VI(t,e=""){let n=String(t);if(!Ig.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
135
- `),o=Mu(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 Cu=/Paste code here if prompted/,zI=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function JI(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!zI.test(o)||Cu.test(t[r]))break;n+=o}return n}function XI(t,e=""){let n=String(t);if(!Cu.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=JI(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 Ru=/OAuth error:/,YI=/Press Enter to retry\./;function ZI(t,e=""){let n=String(t);if(!Ru.test(n)||!YI.test(n)||cr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
137
- `)].reverse().find(a=>Ru.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 QI(t,e=""){let n=String(t);if(!Rg.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
138
- `),o=Mu(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 tR(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])||Ou.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 eR(t){let e=-1;for(let r=t.length-1;r>=0;r--)if($u.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 nR(t,e=""){let n=String(t);if(!Sg.test(n)||!$u.test(n))return null;let r=n.replace(/\r/g,"").split(`
140
- `),o=eR(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||BI(r[N])||xg.test(D))){S.desc=DI(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?jI(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)&&$u.test(n)||Tg.test(n)||Iu.test(n)||Ag.test(n.trimEnd().split(`
141
- `).pop()||"")?!0:cr.test(n)?!1:_g.test(n)&&Eg.test(n)||Ig.test(n)||Cu.test(n)||Ru.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"?WI(e,t.name)||nR(e,t.name)||FI(e,t.name)||KI(e,t.name)||ZI(e,t.name)||XI(e,t.name)||VI(e,t.name)||GI(e,t.name)||QI(e,t.name)||tR(e,t.name)||HI(e,t.name):t.agent==="cursor"||t.agent==="codex"?Kp(t.agent,e,t.name):null}async function Nu(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 rR}from"node:child_process";import{promisify as oR}from"node:util";var Du=oR(rR),Cg=64*1024*1024;async function yt(t,e,{input:n}={}){let r=Du("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 sR(t){try{return await yt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function ps(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)=>Du("git",["-C",t,...a],{maxBuffer:Cg,env:o,...i});try{let a=await sR(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 Du("rm",["-f",r])}catch{}}}async function Bu(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 ju(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 Lu(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 aR(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 Fu(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Uu(t,e,n,r){let{stdout:o}=await yt(t,Fu(["diff","--shortstat",e,n],r));return aR(o)}async function Hu(t,e,n,r){let{stdout:o}=await yt(t,Fu(["diff","--name-only",e,n],r));return o.split(`
144
- `).map(s=>s.trim()).filter(Boolean)}async function Wu(t,e,n,r){let{stdout:o}=await yt(t,Fu(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Pu(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Pu(t,o,["--3way","--whitespace=nowarn"]),a=await iR(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Pu(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Pu(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 iR(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 qu from"node:fs/promises";import Lg from"node:path";import cR from"node:os";var Fg=process.env.BOSTRAT_STATE_DIR||Lg.join(process.env.HOME||cR.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,Gu=!1,Ve={weekly:0,monthly:0},ur=new Map,Yr=new Set,Ku=null,lR=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||!Gu||!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=lR(t);for(let[s,a]of Object.entries(Xr))n>=Ve[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function uR(t,e,n=Date.now()){if(!ce?.caps||!Ku)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 Ku(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 qu.mkdir(Fg,{recursive:!0}),await qu.writeFile(Ug(),JSON.stringify(ce,null,2))}catch{}return ms()}function ms(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:Gu,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}={}){Ku=e||null,fi();try{let o=JSON.parse(await qu.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()),uR(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 Gu=!0,ms()}var di=t=>Math.round(t*1e4)/1e4;import dR from"node:fs/promises";import fR from"node:os";import Yg from"node:path";var mi=t=>new Promise(e=>setTimeout(e,t)),pR=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,mR=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"}],hR=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"]),wR=[{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"?wR: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 gR(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(!hR.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function hs(t,e,{timeoutMs:n=pR}={}){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(mR)}}async function yR(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 bR(t,e){await ga(t),await Et(t,"/model",{enter:!1}),await mi(250),await At(t,["Enter"],{kind:"model-switch"});let n=await hs(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=gR(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 hs(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 hs(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 kR(){let t=Yg.join(process.env.CURSOR_CONFIG_DIR||Yg.join(fR.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await dR.readFile(t,"utf8"))?.model||null}catch{return null}}async function xR(t,e){await ga(t),await ba(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await hs(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 hs(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 kR();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"?bR(e,n):t==="cursor"?xR(e,n):yR(e,n)}var Xt=t=>t||null;function wi(t){let e=t||{},n=vR(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function vR(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(),zu=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;zu(()=>$(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 Vu=new Map;function dy(t,e,n){!n?.promptId||Vu.get(t.id)===n.promptId||(Vu.set(t.id,n.promptId),zu(()=>$(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 Ju(t,e,n,r={}){Vu.delete(t.id),zu(()=>$(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 l$}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,SR=new RegExp(`\\b(?:${py}|${my}|${hy}|${wy})\\b`,"i"),TR=/\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,AR=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,_R=90;function ER(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 ER(n).reverse()){if(!SR.test(r))continue;let o=r.endsWith("?")&&r.length<=_R&&AR.test(r);if(TR.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var yy=["commit","rebase","push","pr"],IR=new RegExp(`\\b(?:${py})\\b`,"i"),RR=new RegExp(`\\b(?:${wy})\\b`,"i"),$R=new RegExp(`\\b(?:${my})\\b`,"i"),OR=new RegExp(`\\b(?:${hy})\\b`,"i"),MR=/\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=[];IR.test(e)&&n.push("commit"),RR.test(e)&&n.push("rebase"),$R.test(e)&&n.push("push");let r=MR.test(e);!r&&OR.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 CR=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<=CR&&n.push({box:r.box,from:r.from,to:e+1e12}),n}var NR=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+NR||(!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 LR}from"node:child_process";import{promisify as FR}from"node:util";import PR from"node:fs";import Xu from"node:fs/promises";import _y from"node:path";import DR from"node:os";var Ey=process.env.BOSTRAT_STATE_DIR||_y.join(process.env.HOME||DR.tmpdir(),".bostrat"),yi=_y.join(Ey,"github-metrics.json"),Iy=60*6e4,Sy=5*6e4,BR=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(PR.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<BR)return;Ty=t;let n=JSON.stringify({windowStart:to,byCaller:Object.fromEntries(ze),lastHour:Tn});Ay=Ay.then(()=>Xu.mkdir(Ey,{recursive:!0})).then(()=>Xu.writeFile(`${yi}.tmp`,n)).then(()=>Xu.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 jR=/rate limit|too quickly|abuse detection|HTTP 429/i;function An(t){return jR.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,ws=new Map;function xi(t,e,n){let{state:r,reason:o}=nl(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of ws)a-c.at>=My()&&ws.delete(i);ws.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=ws.get(t);return s?Date.now()-s.at>=My()?(ws.delete(t),!1):s.busy:!1}async function Yu(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 Yu(t,e)).working}var Cy=FR(LR),Ny=[4e3,15e3,45e3],UR=2500,HR=5*6e4;async function Zu(t,e,{midTurn:n=vi,pollMs:r=UR,capMs:o=HR}={}){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 WR=15e3,qR=9e4,KR=2e3,GR=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,VR=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,zR=/\s--web\b/,Py=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,JR=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,XR=/^track\/.+-([0-9a-f]{8})$/;function YR(t,e){let n=String(t||"").match(XR);return!!n&&n[1]!==e}function Qu(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Dy(t){let e=String(t||""),n=e.match(GR);if(!n)return null;let r=Qu(e,n.index);return zR.test(r)?null:{repo:r.match(Py)?.[1]??null,head:r.match(JR)?.[1]??null}}var ZR=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(VR);if(!n)return null;let r=Qu(e,n.index),o=Qu(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("-")){ZR.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 QR="number,url,title,headRefName,createdAt";async function t$(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",QR],{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-WR&&!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 e$(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 n$(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)||YR(c.headRefName,t.id),i=[];for(let c of await e$(t,e?.repo??null)){let l;try{l=await t$(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 n$(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 is(f,d.number))?null:(await Zu(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 r$="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 cs(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 Zu(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",r$],{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 Zu(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 is(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??qR,M=o?.pollMs??KR,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 o$ from"node:fs/promises";import ed from"node:path";import s$ from"node:os";var a$=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/;function td(t,e){let n=new RegExp(`\\b${e}="([^"]*)"`).exec(t);return n?n[1]:null}function Si(t){let e=a$.exec(String(t||""));if(!e)return null;let[,n,r]=e,o=td(n,"from"),s=o?o.replace(/^uds:/,""):null,a=Uy(s);return{socket:s,pid:a,name:td(n,"from-name"),mode:td(n,"from-mode"),text:r.trim()}}function Uy(t){let e=/(\d+)\.sock$/.exec(String(t||""));return e?Number(e[1]):null}function i$(){return process.env.CLAUDE_CONFIG_DIR?ed.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):ed.join(s$.homedir(),".claude","sessions")}async function c$(t){let e=Uy(t);if(!e)return null;try{let n=await o$.readFile(ed.join(i$(),`${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 c$(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 gs(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 nd=new Map,Ti=new Map;async function rd(t){return nd.has(t.id)?nd.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(gs({role:o,sourceId:r.payload.sourceId})),n.keys.add(gs({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(gs({role:"user",sourceId:r.payload.sourceId})),n.keys.add(gs({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 nd.set(t.id,n),n}).finally(()=>Ti.delete(t.id))),Ti.get(t.id))}async function Ky(t,e){try{let n=(await rd(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=gs(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 ys(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:l$(),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 rd(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 rd(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 u$(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 od=new Map;async function d$(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?.codexTs!=null&&o.add(s.payload.codexTs);od.set(n,o)}return od.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 d$(t,e),o=u$(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 ad from"node:path";import f$ from"node:os";var p$=process.env.BOSTRAT_STATE_DIR||ad.join(process.env.HOME||f$.tmpdir(),".bostrat"),Xy=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,m$="https://cursor.com/api/dashboard/get-filtered-usage-events",Jy=100;async function h$({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(m$,{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 w$(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 g$=5*6e4,$n={at:0,rows:null,error:null},Ei=()=>ad.join(p$,"cursor-usage.json");function Yy(){try{return JSON.parse(_i.readFileSync(Ei(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function y$(t){try{_i.mkdirSync(ad.dirname(Ei()),{recursive:!0});let e=Ei()+".tmp";_i.writeFileSync(e,JSON.stringify(t)),_i.renameSync(e,Ei())}catch{}}var b$=5e3;async function k$({fetchImpl:t,now:e=Date.now()}={}){if($n.rows&&e-$n.at<g$)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 h$({sinceMs:o,cookie:n,fetchImpl:t});$n={at:e,rows:s.map(w$).filter(Boolean),error:null}}catch(s){$n={at:e,rows:null,error:String(s?.message||s)}}return $n.rows}var id=()=>({enabled:Xy(),lastPollAt:$n.at||null,error:$n.error}),sd=new Map;async function x$(t,e){let n=`${t.id}:${e.name}`;if(!sd.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);sd.set(n,o)}return sd.get(n)}async function Zy(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Xy())return;let r=await k$({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 x$(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}})}y$({sinceMs:c,seen:[...i].slice(-b$)})}catch{}}import{execFile as _$}from"node:child_process";import{promisify as E$}from"node:util";import Qy from"node:fs/promises";import tb from"node:path";import v$ from"node:os";var eb=process.env.BOSTRAT_STATE_DIR||tb.join(process.env.HOME||v$.tmpdir(),".bostrat"),S$=tb.join(eb,"github-latency.ndjson"),T$=100,Ii=[],cd=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>T$&&Ii.shift();let u=cd.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)),cd.set(t,u),A$(l),l}async function A$(t){try{await Qy.mkdir(eb,{recursive:!0}),await Qy.appendFile(S$,JSON.stringify(t)+`
151
- `)}catch{}}function nb(){let t={};for(let[e,n]of cd)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 I$(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=E$(_$),R$={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,...R$,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw An(a)&&_n(a),a}}var $i=null;async function dd(){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 Io();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return $i={at:Date.now(),data:t},t}async function $$(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 ld(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 fd="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",sb=fd.replace("statusCheckRollup,","");function pd(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?{...ld([]),unavailable:!0}:ld(ib(n))}function Oi(t,e=ld(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 O$=9e4,eo=new Map;function M$(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)<O$}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",fd]);return Oi(JSON.parse(o))}catch(o){if(!pd(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 ks(t,{caller:e="prs"}={}){try{let n=await re(t,["pr","list","--json",fd,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>Oi(r)).sort(rb)}catch(n){if(!pd(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 C$=3e4,N$=1500,bs=new Map;function Re(){bs.clear()}async function P$(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 dd();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Oe())await $$(a.path)&&e.push(a);let n=Date.now(),r=ki(n)>0,o=await Promise.all(e.map(async a=>{let i=bs.get(a.path),c=i?.pending?N$:C$;if(i&&(r||n-i.at<c))return i.entry;try{let l=await et(a.path).catch(()=>null),u=M$(a.path,await ks(a.path,{caller:"tab"}),n);if(await P$(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 bs.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 bs.keys())s.has(a)||bs.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 _o(t)}var xs=new Set(["squash","merge","rebase"]);async function oo(t,e,{method:n="squash"}={}){let r=await Xe(t);if(!xs.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 Tc().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 vs(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 md(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 D$=2500,B$=9e4,j$=t=>new Promise(e=>setTimeout(e,t));function hd(t){return t.green&&!t.isDraft}function L$(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"}:hd(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 ud=new Map;function fb(){let t={};for(let[e,n]of ud)t[e]={...n};return t}async function pb(t,{method:e="merge",onMerged:n=null}={}){let r=await Xe(t);if(!xs.has(e))throw new Error(`invalid merge method: ${e}`);if(ud.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await ks(r.path)).filter(hd).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 ud.set(r.path,s),U$(s,o,{listPrs:()=>ks(r.path),merge:a=>oo(r.path,a,{method:e}),sleep:j$,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function F$(t,e){let n=Date.now()+(e.settleMaxMs??B$),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(D$)}}async function U$(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 F$(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(!hd(a)){t.skipped.push({number:a.number,title:a.title,reason:L$(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 wd(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 I$(t);let o=n||"";if((await me({cwd:t})).GH_TOKEN){let l=await Tc().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(!pd(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=H$(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await re(n.path,["run","view",c,"--log-failed"]).then(u=>W$(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function H$(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function W$(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
137
+ `)}catch{return""}}async function J$(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await V$(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await $b(t.slug)||"server exited before listening"),Ye.delete(t.id),await Xe(t,t.state,t.error?{error:t.error}:{}),!0}if(await Fs(t.port))return Ye.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await Xe(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>z$?(t.state="error",t.error=await $b(t.slug)||"never started listening",await Xe(t,"error",{error:t.error}),!0):!1;let o=(Ye.get(t.id)||0)+1;return Ye.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}async function tc(){if(!jd){jd=!0;try{let t=await Ze(),e=!1;for(let n of t.views)await J$(n)&&(e=!0);e&&await Oe()}finally{jd=!1}}}async function Vd({trackId:t}={}){await tc();let e=await Ze();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function Lb(t){let n=(await Ze()).views.find(r=>r.id===t);return n?{...n}:null}function zd(t){return Re&&Re.views.find(e=>e.slug===t)||null}async function Jd({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=Xt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await W$(r,u),await q$(u,n)},f=await Ze();if(i!=="detected"){let w=Ud(e),b=String(n).trim(),k=f.views.find(S=>S.trackId===t&&(S.slug===w||Ud(S.name)===w||S.cmd===b&&Xt.resolve(S.serveCwd??S.cwd)===u));if(k)return Y$(f,k,{cmd:b,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(w=>w.state==="live"||w.state==="starting").length>=jn)throw new Error(`view cap reached (${jn} live) \u2014 stop one first`);let m={id:_$().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:K$(f,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await G$(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Gd(m),f.views.push(m),await Oe(),await Xe(m,"created",{origin:i}),Fd(t,m.id),i==="detected"&&await Fs(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await Oe(),await Xe(m,"live")),{...m}}async function Y$(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 U$(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Fs(e.port))return await Oe(),Fd(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(d=>d.id!==e.id&&(d.state==="live"||d.state==="starting")).length>=jn)throw new Error(`view cap reached (${jn} live) \u2014 stop one first`);return e.origin==="detected"?(await Kd(e.port),e.origin="agent"):await Us(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Ye.delete(e.id),await Gd(e),await Oe(),await Xe(e,"created",{restarted:!0,reused:!0}),Fd(e.trackId,e.id),{...e,reused:!0}}async function Fb(t){let n=(await Ze()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await Kd(n.port):await Us(n.slug),n.state!=="stopped"&&(n.state="stopped",Ye.delete(n.id),await Oe(),await Xe(n,"stopped")),{...n}}async function Ub(t){let e=await Ze(),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>=jn)throw new Error(`view cap reached (${jn} live) \u2014 stop one first`);return await Us(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Ye.delete(n.id),await Gd(n),await Oe(),await Xe(n,"created",{restarted:!0}),{...n}}async function Hb(t){let e=await Ze(),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 Kd(r.port):await Us(r.slug),e.views.splice(n,1),Ye.delete(r.id),await Oe(),await Xe(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function Wb(t){let e=await Ze(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Us(r.slug),Ye.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await Oe()),n.length}async function qb(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await Ze(),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 Oe()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(X$)){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(!jb().has(c)&&!n.views.some(l=>l.port===c)&&await Fs(c))try{i.push(await Jd({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}var $e,$$,Ob,_b,Ib,jn,O$,yo,Rb,Bs,C$,Ld,M$,P$,N$,Hd,Cb,Dd,j$,F$,Pb,Zi,Re,Bd,js,Wd,Nb,Ye,z$,jd,X$,ec=C(()=>{Ut();ct();wn();$e=R$(I$),$$=process.env.BOSTRAT_STATE_DIR||Xt.join(process.env.HOME||A$.tmpdir(),".bostrat"),Ob=Xt.join($$,"views.json"),[_b,Ib]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),jn=Number(process.env.BOSTRAT_VIEW_MAX)||3,O$="bostrat-view-",yo=t=>`${O$}${t}`,Rb=new Set(["bostrat","www","api","views","app"]),Bs=t=>Object.assign(new Error(t),{status:400}),C$=new Set(["npm","pnpm","yarn","bun"]),Ld="package.json",M$=/[|;&`]|\$\(/,P$=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),N$=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Hd=t=>String(t).trim().split(/\s+/).filter(Boolean),Cb=/^[A-Za-z_][A-Za-z0-9_]*=/;Dd=t=>t==="/",j$=t=>t===".."||t.startsWith("../")||t.includes("/../")||t.endsWith("/..");F$=t=>Ls.stat(t).then(e=>e.isDirectory(),()=>!1),Pb=t=>Ls.stat(t).then(e=>e.isFile(),()=>!1),Zi=t=>Ls.realpath(t).catch(()=>Xt.resolve(t));Re=null,Bd=null;js=new Map,Wd=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Nb=12e3;Ye=new Map,z$=12e4;jd=!1;X$=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});import Kb from"node:fs/promises";import Gb from"node:path";import Z$ from"node:os";function rc({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};nc.push(l),nc.length>tO&&nc.shift();let u=Yd.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)),Yd.set(t,u),eO(l),l}async function eO(t){try{await Kb.mkdir(Vb,{recursive:!0}),await Kb.appendFile(Q$,JSON.stringify(t)+`
138
+ `)}catch{}}function zb(){let t={};for(let[e,n]of Yd)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:nc.slice(-20)}}var Vb,Q$,tO,nc,Yd,oc=C(()=>{Vb=process.env.BOSTRAT_STATE_DIR||Gb.join(process.env.HOME||Z$.tmpdir(),".bostrat"),Q$=Gb.join(Vb,"github-latency.ndjson"),tO=100,nc=[],Yd=new Map});import{execFile as nO}from"node:child_process";import{promisify as rO}from"node:util";async function oO(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([ko("git",["-C",t,"rev-parse","HEAD"]),ko("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{}}async function ae(t,e,n={}){let{caller:r="prs",...o}=n,s=await Te({cwd:t});Ve(r);try{let{stdout:a}=await ko("gh",e,{cwd:t,...sO,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw On(a)&&Cn(a),a}}async function Qd(){if(sc&&Date.now()-sc.at<6e4)return sc.data;let t;try{Ve("auth");let e=await ko("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"||e?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await es();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return sc={at:Date.now(),data:t},t}async function aO(t){try{let{stdout:e}=await ko("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function 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 Xd(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}}function ef(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Zb(t,e){if(!e)return null;let n=await rt(t);if(!n)return null;try{let r=await ae(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 Qb(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 tk(t,e){let n=await Zb(t,e);return n===null?{...Xd([]),unavailable:!0}:Xd(Qb(n))}function ac(t,e=Xd(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}}function cO(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){bo.delete(s);continue}r.add(s),bo.has(s)||bo.set(s,n),o.mergeabilityPending=n-bo.get(s)<iO}for(let o of bo.keys())o.startsWith(`${t}#`)&&!r.has(o)&&bo.delete(o);return e}async function xo(t,e){let n=await Qe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await ae(n.path,["pr","view",String(r),"--json",tf]);return ac(JSON.parse(o))}catch(o){if(!ef(o))throw o;let s=await ae(n.path,["pr","view",String(r),"--json",Xb]),a=JSON.parse(s);return ac(a,await tk(n.path,a.headRefOid))}}async function kr(t,{caller:e="prs"}={}){try{let n=await ae(t,["pr","list","--json",tf,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>ac(r)).sort(Jb)}catch(n){if(!ef(n))throw n;let r=await ae(t,["pr","list","--json",Xb,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>ac(o,await tk(t,o.headRefOid))))).sort(Jb)}}function Me(){Hs.clear()}async function dO(t,e){if(!t)return e;for(let n of e){let r=await Ht(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 Ui(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Wi(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function ek(){let t=await Qd();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Fe())await aO(a.path)&&e.push(a);let n=Date.now(),r=Li(n)>0,o=await Promise.all(e.map(async a=>{let i=Hs.get(a.path),c=i?.pending?uO:lO;if(i&&(r||n-i.at<c))return i.entry;try{let l=await rt(a.path).catch(()=>null),u=cO(a.path,await kr(a.path,{caller:"tab"}),n);if(await dO(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&rc({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},f=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return Hs.set(a.path,{at:Date.now(),entry:d,pending:f}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Ce(l)}}})),s=new Set(e.map(a=>a.path));for(let a of Hs.keys())s.has(a)||Hs.delete(a);return{auth:t,repos:o}}async function Qe(t){let e=(await Fe()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function xr(t){return Qo(t)}async function So(t,e,{method:n="squash"}={}){let r=await Qe(t);if(!Ws.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 Te({cwd:r.path})).GH_TOKEN){let a=await yl().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await ae(r.path,s)}catch(a){throw new Error(Ce(a))}finally{Me()}return{ok:!0,number:o}}async function qs(t,e){let n=await Qe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ae(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Ce(s))}finally{Me()}let o=await rt(n.path).catch(()=>null);return o&&await Xy(o,r).catch(()=>{}),{ok:!0,number:r}}async function nf(t,e){let n=await Qe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ae(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Ce(o))}finally{Me()}return{ok:!0,number:r}}function rf(t){return t.green&&!t.isDraft}function hO(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 nk(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:rf(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function rk(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}function ok(){let t={};for(let[e,n]of Zd)t[e]={...n};return t}async function sk(t,{method:e="merge",onMerged:n=null}={}){let r=await Qe(t);if(!Ws.has(e))throw new Error(`invalid merge method: ${e}`);if(Zd.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await kr(r.path)).filter(rf).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 Zd.set(r.path,s),gO(s,o,{listPrs:()=>kr(r.path),merge:a=>So(r.path,a,{method:e}),sleep:mO,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function wO(t,e){let n=Date.now()+(e.settleMaxMs??pO),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(fO)}}async function gO(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 wO(e.slice(o).map(i=>i.number),n)}catch(i){for(let c of e.slice(o))t.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${Ce(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){t.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!rf(a)){t.skipped.push({number:a.number,title:a.title,reason:hO(a)});continue}try{await n.merge(a.number),t.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){t.failed.push({number:a.number,title:a.title,reason:Ce(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Me()}}async function of(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await ko("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Ce(l)}`)}await oO(t);let o=n||"";if((await Te({cwd:t})).GH_TOKEN){let l=await yl().catch(()=>null);l&&(o=`${o?`${o}
139
+
140
+ `:""}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 ae(t,s)}catch(l){throw new Error(Ce(l))}finally{Me()}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 ak(t,e){let n=await Qe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await ae(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Ce(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!ef(i))throw i;let c=await ae(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Zb(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Ce(i));o=Qb(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=yO(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await ae(n.path,["run","view",c,"--log-failed"]).then(u=>bO(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function yO(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function bO(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
154
141
  `);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
155
142
  `+o.slice(-e).join(`
156
143
  `)),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 q$=6e3,gd=new Map,K$=30*6e4;function yd(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>K$?null:n}async function hb(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await B(t)}catch{return}if(yd(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-(gd.get(t.id)||0)<q$)return;gd.set(t.id,o);let s;try{s=await ks(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 is(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(()=>{}),gd.delete(t.id)}import G$ from"node:fs/promises";import kb from"node:path";import V$ from"node:os";import{randomUUID as xb}from"node:crypto";var z$=process.env.BOSTRAT_STATE_DIR||kb.join(process.env.HOME||V$.tmpdir(),".bostrat"),vb=kb.join(z$,"flows.json"),wb=20,gb=80,yb=500,bb=80,Ye=null,bd=null;async function Ze(){return Ye||(bd||=(async()=>{let t=!0;try{Ye=JSON.parse(await G$.readFile(vb,"utf8"))}catch{Ye={flows:[]},t=!1}finally{bd=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})(),bd)}async function so(){await Ft(vb,Ye)}var Mi=t=>Pt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function kd(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 J$(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 X$(t){let n=(await Ze()).flows.find(r=>r.id===t);return n?{...n}:null}async function Ab(t){let e=await X$(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=J$(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 kd(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 kd(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 kd(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 xd from"node:fs/promises";import Ss from"node:path";import{randomUUID as Y$}from"node:crypto";var Nb=".bostrat/uploads",vd=t=>Ss.join(t,Nb),Cb=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,Z$=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Q$=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 Sd(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return Z$.has(e)?"image":Q$.has(e)?"doc":null}function tO(t){let r=Ss.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=tO(e),o=Ss.extname(r).slice(1),s=Sd(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Y$().slice(0,8),i=vd(t);await xd.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await xd.writeFile(Ss.join(i,c),n,{mode:384}),await xd.chmod(Ss.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 eO=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Td(t){return t.role==="user"&&eO.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 oO=rO(nO),Qe=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),qb=U.join(Qe,"tracks.json"),sO=U.join(Qe,"events.jsonl"),Od=U.join(Qe,"workspaces"),aO=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 iO(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 Ms(t){let e=Di[t];if(e)return process.env[e.envVar]||e.base}var On={at:0,value:null};async function Nd({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(Ms(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=iO(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.":qc[r]?`Install it on this node: ${qc[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",cO=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;function lO(t,e=Li){return`${t.trim()}
144
+ `+r.slice(-n)),r}function Ce(t){return String(t?.stderr||t?.message||t||"").trim().split(`
145
+ `).find(n=>n.trim())||"gh failed"}var ko,sO,sc,tf,Xb,iO,bo,Jb,lO,uO,Hs,Ws,fO,pO,mO,Zd,Ks=C(()=>{Zn();oe();Nn();Rs();oc();ct();ko=rO(nO),sO={timeout:3e4,maxBuffer:8*1024*1024};sc=null;tf="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Xb=tf.replace("statusCheckRollup,","");iO=9e4,bo=new Map;Jb=(t,e)=>t.number-e.number;lO=3e4,uO=1500,Hs=new Map;Ws=new Set(["squash","merge","rebase"]);fO=2500,pO=9e4,mO=t=>new Promise(e=>setTimeout(e,t));Zd=new Map});function af(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,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}: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>xO?null:n}async function ik(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await F(t)}catch{return}if(af(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Gi(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(sf.get(t.id)||0)<kO)return;sf.set(t.id,o);let s;try{s=await kr(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 ee({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 $s(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(()=>{}),sf.delete(t.id)}var kO,sf,xO,cf=C(()=>{ct();Ks();Zn();Nn();Vi();kO=6e3,sf=new Map,xO=30*6e4});import lf from"node:os";import{execFile as SO}from"node:child_process";import{promisify as vO}from"node:util";async function ic(){if(process.platform!=="darwin")return lf.freemem();try{let{stdout:t}=await TO("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:lf.freemem()}catch{return lf.freemem()}}var TO,uf=C(()=>{TO=vO(SO)});import Et from"node:fs/promises";import It from"node:path";import uk from"node:os";import{randomUUID as AO}from"node:crypto";import{execFile as EO}from"node:child_process";import{promisify as _O}from"node:util";import{chromium as IO}from"playwright-core";async function De(){return Sr||(df||=(async()=>{try{Sr=JSON.parse(await Et.readFile(fk,"utf8"))}catch{Sr={mocks:[]}}finally{df=null}return Sr.mocks||=[],Sr})(),df)}async function ie(){await zt(fk,Sr)}function hk(t,e){Ne(t,e.v,e.videoRel?.endsWith(".webm")?"webm":"mp4"),e.posterRel&&Ne(t,e.v,"png")}function Wt(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(()=>{})}function ce(t,e){let n=Vs(t.cwd);return{html:It.join(n,`${t.id}.v${e}.html`),png:It.join(n,`${t.id}.v${e}.png`)}}function Gs(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?It.join(t.cwd,r):null}function be(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 mf(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function cc(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(BO)}function jO(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 LO(t){let e=Buffer.alloc(24),n=await Et.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function FO({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 UO(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,b=0;b<o;b++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(b<u&&(u=b),b>f&&(f=b),m<d&&(d=m),p=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:p}}},e.toString("base64"))}async function HO(t,e,{bg:n,crop:r}){let o=await t.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function WO({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await IO.launch({executablePath:$O,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new ye("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:vo})).newPage();a.setDefaultTimeout(To),await a.goto(`file://${t}`,{waitUntil:"load",timeout:To}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:To});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await UO(a,i),u=l&&FO({...l,pad:MO*vo,align:vo});u&&(i=await HO(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<pk)throw new ye(`capture produced a ${i.length}-byte image`);let c=mf(i);if(!c)throw new ye("output is not a PNG");return await Et.writeFile(e,i),{width:Math.round(c.width/vo),height:Math.round(c.height/vo),dpr:vo,renderMs:Date.now()-r}}catch(s){throw s instanceof ye?s:new ye(/timeout/i.test(s?.message||"")?`render timed out after ${To}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function lk({htmlPath:t,outPath:e,width:n}){let r=await Et.mkdtemp(It.join(uk.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await pf(OO,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:To,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new ye(c?.killed?`render timed out after ${To}ms`:"firefox exited non-zero",s)}let a=await Et.stat(e).catch(()=>null);if(!a||a.size<pk)throw new ye(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=mf(await LO(e));if(!i)throw new ye("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Et.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function qO(t){if(RO==="firefox")return lk(t);try{return await WO(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return lk(t);throw e}}function wk(t){return ff=ff.then(t,t),ff}async function gk(t,e){let n=await De(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await ie(),await Wt(r,"rendering",{version:e});try{let o=await ic();if(o<PO)throw new ye(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=ce(r,e),a=await KO({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await Et.rm(s.png,{force:!0}).catch(()=>{});return}hf(r,{v:e,source:"html",htmlRel:It.relative(r.cwd,s.html),pngRel:It.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await ie(),await Wt(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),Pe(r),Ne(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await ie(),await Wt(r,"error",{version:e,error:r.error}),Pe(r)}}}function hf(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 GO({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?CO:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function zs({trackId:t,group:e}={}){let n=await De(),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 tn(t){let n=(await De()).mocks.find(r=>r.id===t);return n?{...n}:null}function yk({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&&!cc(e))throw new Error("image must be a PNG");if(n){let o=jO(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>ck)throw new Error(`video must be ${ck/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function bk({trackId:t,name:e,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=GO({trackId:t,name:e,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=yk({html:n,image:r,video:o}),m=await De(),w={id:AO().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:d,height:null,dpr:null,state:"queued",source:f?"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 Et.mkdir(Vs(s),{recursive:!0}),m.mocks.push(w),f?(await Et.writeFile(ce(w,1).html,n),await ie(),await Wt(w,"created"),Pe(w),Ne(w,1,"html"),wk(()=>gk(w.id,1))):o?(await xk(w,1,o,p),await ie(),await Wt(w,"created"),await Wt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),Pe(w),hk(w,w.versions[0])):(await Sk(w,1,r),await ie(),await Wt(w,"created"),await Wt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),Pe(w),Ne(w,1,"png")),{...w}}async function kk(t,{html:e,image:n,video:r,width:o}={}){let a=(await De()).mocks.find(u=>u.id===t);if(!a)throw new Error("mock not found");let i=typeof e=="string"&&e.trim(),c=yk({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 Et.mkdir(Vs(a.cwd),{recursive:!0}),i?(a.state="queued",await Et.writeFile(ce(a,l).html,e),await ie(),await Wt(a,"updated",{version:l}),Pe(a),Ne(a,l,"html"),wk(()=>gk(a.id,l))):r?(await xk(a,l,r,c),await ie(),await Wt(a,"updated",{version:l}),await Wt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:be(a)?.durationS??null}),Pe(a),hk(a,a.versions.find(u=>u.v===l))):(await Sk(a,l,n),await ie(),await Wt(a,"updated",{version:l}),await Wt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Pe(a),Ne(a,l,"png")),{...a}}async function VO(t){try{let{stdout:e}=await pf(NO,["-v","error","-print_format","json","-show_format","-show_streams",t],{timeout:mk,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 zO(t,e){try{await pf(DO,["-y","-v","error","-i",t,"-frames:v","1","-f","image2",e],{timeout:mk,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Et.stat(e).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function xk(t,e,n,r){let o=Vs(t.cwd),s=It.join(o,`${t.id}.v${e}.${r.ext}`),a=It.join(o,`${t.id}.v${e}.poster.png`);await Et.writeFile(s,n);let i=await VO(s),c=await zO(s,a);hf(t,{v:e,source:"video",htmlRel:null,pngRel:null,videoRel:It.relative(t.cwd,s),posterRel:c?It.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 Sk(t,e,n){let r=mf(n);if(!r)throw new Error("image must be a PNG");let o=ce(t,e);await Et.writeFile(o.png,n),hf(t,{v:e,source:"image",htmlRel:null,pngRel:It.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function vk(t){let e=await De(),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 Wt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Tk(t){let e=await De(),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=ce(r,o);await Et.rm(s.html,{force:!0}).catch(()=>{}),await Et.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Et.rm(It.join(r.cwd,s),{force:!0}).catch(()=>{});return await ie(),await Wt(r,"deleted"),pe("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Ak(t){let n=(await De()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=It.join(dk,"mock-archive",t.id),o=Vs(t.cwd),s=It.join(r,".bostrat","mocks");await Et.mkdir(s,{recursive:!0});let a=await Et.readdir(o).catch(()=>[]);for(let i of a)await Et.copyFile(It.join(o,i),It.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 Et.stat(It.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(),Pe(i)}return await ie(),n.length}async function Ek(t){let e=await De(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await ie();for(let r of n)pe("mocks",r.id).catch(()=>{})}return n.length}async function _k(){if(!gt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await De();for(let r of n.mocks){await Pe(r);for(let o of r.versions||[])o.htmlRel&&await Ne(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Ne(r,o.v,"png"),o.videoRel&&await Ne(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var pf,dk,fk,RO,$O,OO,CO,vo,MO,pk,To,PO,ck,NO,DO,mk,ye,Sr,df,Pe,Ne,Vs,BO,KO,ff,Ao=C(()=>{ct();wn();uf();yn();pf=_O(EO),dk=process.env.BOSTRAT_STATE_DIR||It.join(process.env.HOME||uk.tmpdir(),".bostrat"),fk=It.join(dk,"mocks.json"),RO=process.env.BOSTRAT_MOCK_ENGINE||"chromium",$O=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",OO=process.env.BOSTRAT_FIREFOX_BIN||"firefox",CO=1300,vo=2,MO=32,pk=1500,To=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,PO=700*1024*1024,ck=100*1024*1024,NO=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",DO=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",mk=2e4,ye=class extends Error{constructor(e,n=""){super(n?`${e}
146
+ ${n.trim().split(`
147
+ `).slice(-6).join(`
148
+ `)}`:e),this.name="MockRenderError"}},Sr=null,df=null;Pe=t=>Ft("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Ne=(t,e,n)=>{let r=(t.versions||[]).find(s=>s.v===e),o=n==="mp4"||n==="webm"?Gs(t,r,"video"):r&&Gs(t,r,n)||(n==="html"?ce(t,e).html:null);return o?gn("mocks",t.id,e,n,o).catch(()=>{}):Promise.resolve()};Vs=t=>It.join(t,".bostrat","mocks");BO=Buffer.from([137,80,78,71,13,10,26,10]);KO=qO,ff=Promise.resolve()});import JO from"node:fs/promises";import Ck from"node:path";import YO from"node:os";import{randomUUID as Mk}from"node:crypto";async function nn(){return en||(wf||=(async()=>{let t=!0;try{en=JSON.parse(await JO.readFile(Pk,"utf8"))}catch{en={flows:[]},t=!1}finally{wf=null}if(en.flows||=[],!t&&gt()){let{records:e,source:n}=await Ot("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&en.flows.push(r.data);en.flows.length&&await Eo().catch(()=>{})}}return en})(),wf)}async function Eo(){await zt(Pk,en)}function gf(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 Nk({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>Ik)throw new Error(`steps must be an array of 1-${Ik}`);let r=await zs({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>$k)throw new Error(`${l}: caption over ${$k} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>Ok)throw new Error(`${l}: title over ${Ok} chars`);let f=String(c.mock??"").trim().replace(/^mock:/,"");if(!f)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===f);if(p.length||(p=r.filter(S=>S.name.toLowerCase()===f.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${f}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${f}" is ambiguous (${p.map(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let m=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(S=>S.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let b=o.findIndex(S=>S.mockId===m.id),k=b>=0?o.splice(b,1)[0].id:Mk().slice(0,8);s.push({id:k,mockId:m.id,caption:u,title:d,v:w})}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 ZO(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Rk)throw new Error(`name over ${Rk} chars`);return t.trim()}async function Dk({trackId:t}={}){let e=await nn();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function QO(t){let n=(await nn()).flows.find(r=>r.id===t);return n?{...n}:null}async function Bk(t){let e=await QO(t);if(!e)return null;let n=new Map((await zs({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=be(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 jk({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=ZO(e),i=await nn(),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 Nk({trackId:t,steps:n}),d=Date.now(),f={id:Mk().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:d}],steps:l,createdAt:d,updatedAt:d};return i.flows.push(f),await Eo(),await gf(f,"created"),lc(f),{...f}}async function Lk(t,{steps:e,note:n}={}){let o=(await nn()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Nk({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 Eo(),await gf(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),lc(o),{...o}}async function Fk(t){let e=await nn(),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 Eo(),await gf(r,"deleted"),pe("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Uk(t){return(await nn()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function Hk(t){let n=(await nn()).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(),lc(r);return n.length&&await Eo(),n.length}async function Wk(t){let e=await nn(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await Eo();for(let r of n)pe("flows",r.id).catch(()=>{})}return n.length}async function qk(){if(!gt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await nn();for(let r of n.flows)await lc(r);return{pushed:n.flows.length,source:"plane"}}var XO,Pk,Ik,Rk,$k,Ok,en,wf,lc,yf=C(()=>{ct();wn();Ao();yn();XO=process.env.BOSTRAT_STATE_DIR||Ck.join(process.env.HOME||YO.tmpdir(),".bostrat"),Pk=Ck.join(XO,"flows.json"),Ik=20,Rk=80,$k=500,Ok=80,en=null,wf=null;lc=t=>Ft("flows",{...t},{trackId:t.trackId}).catch(()=>{})});import sn from"node:fs/promises";import an from"node:path";import tC from"node:os";import{randomUUID as eC}from"node:crypto";function Js(t){return nC.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}async function Be(){return rn||(bf||=(async()=>{let t=!0;try{rn=JSON.parse(await sn.readFile(Gk,"utf8"))}catch{rn={runbooks:[]},t=!1}finally{bf=null}return rn.runbooks||=[],!t&&gt()&&await rC().catch(()=>{}),rn})(),bf)}async function Tr(){await zt(Gk,rn)}function dc(t){return Ys(Ft("runbooks",{...t},{trackId:t.trackId}))}async function rC(){let{records:t,source:e}=await Ot("runbooks");if(!(e!=="plane"||!t.length)){await sn.mkdir(uc,{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 is("runbooks",r.id,o.v,"md");typeof s=="string"&&await sn.writeFile(on(r,o.v),s).catch(()=>{})}rn.runbooks.push(r)}rn.runbooks.length&&await Tr()}}function _o(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 on(t,e){return an.join(uc,`${t.id}.v${e}.md`)}function Vk(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 zk(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>Kk)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${Kk/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Js(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 oC(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function sC({trackId:t,status:e}={}){let n=await Be(),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 Jk({trackId:t,status:e}={}){let n=await Be(),r=await sC({trackId:t,status:e});if(!gt())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Ot("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 kf(t){let e=await aC(t);if(e)return e;if(!gt())return null;let{records:n,source:r}=await Ot("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 Yk(t,e){let n=await is("runbooks",t.id,e,"md");return typeof n=="string"?n:null}async function aC(t){let n=(await Be()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Xk({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=zk(r),c=await Be(),l={id:eC().slice(0,8),trackId:t,trackName:e||t,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await sn.mkdir(uc,{recursive:!0}),await sn.writeFile(on(l,1),r),l.versions.push({v:1,mdRel:an.relative(vr,on(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Tr(),await _o(l,"created",{version:1}),dc(l),Ys(gn("runbooks",l.id,1,"md",on(l,1))),{...l}}async function Zk(t,{md:e,note:n}={}){let o=(await Be()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");oC(o,"update");let s=zk(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await sn.mkdir(uc,{recursive:!0}),await sn.writeFile(on(o,a),e),o.versions.push({v:a,mdRel:an.relative(vr,on(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Tr(),await _o(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),dc(o),Ys(gn("runbooks",o.id,a,"md",on(o,a))),{...o}}async function Qk(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Be()).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 Tr(),await _o(r,e==="done"?"done":"reopened"),dc(r),{...r})}async function tx(t){let e=await Be(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await sn.rm(an.join(vr,o.mdRel),{force:!0}).catch(()=>{});return await Tr(),r.trackDeletedAt||await _o(r,"deleted"),Ys(pe("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function ex(t){let n=(await Be()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await _o(r,"orphaned"),r.cwd=null,dc(r);return n.length&&await Tr(),n.length}async function nx(t){let e=await Be(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await _o(r,"deleted");for(let o of r.versions||[])await sn.rm(an.join(vr,o.mdRel),{force:!0}).catch(()=>{});Ys(pe("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Tr()),n.length}async function rx(){if(!gt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Be();for(let r of n.runbooks){await Ft("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await gn("runbooks",r.id,o.v,"md",an.join(vr,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var vr,Gk,uc,Kk,nC,rn,bf,Ys,fc=C(()=>{wn();ct();yn();vr=process.env.BOSTRAT_STATE_DIR||an.join(process.env.HOME||tC.tmpdir(),".bostrat"),Gk=an.join(vr,"runbooks.json"),uc=an.join(vr,"runbooks"),Kk=256*1024,nC=[{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/}];rn=null,bf=null;Ys=t=>Promise.resolve(t).catch(()=>{})});import xf from"node:fs/promises";import Xs from"node:path";import{randomUUID as iC}from"node:crypto";function vf(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return cC.has(e)?"image":lC.has(e)?"doc":null}function uC(t){let r=Xs.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 ax(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>ox)throw new Error(`file too large \u2014 max ${Math.floor(ox/(1024*1024))} MB`);let r=uC(e),o=Xs.extname(r).slice(1),s=vf(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=iC().slice(0,8),i=Sf(t);await xf.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await xf.writeFile(Xs.join(i,c),n,{mode:384}),await xf.chmod(Xs.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${sx}/${c}`,kind:s,size:n.length}}function ix(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}function Tf(t){return t.role==="user"&&dC.test((t.text||"").trim())}var sx,Sf,ox,cC,lC,dC,Af=C(()=>{sx=".bostrat/uploads",Sf=t=>Xs.join(t,sx),ox=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,cC=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),lC=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"]);dC=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Ar(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var cx=C(()=>{});var Hx={};dl(Hx,{HANDOFF_LATEST_REL:()=>la,KNOWN_AGENTS:()=>ia,addTrackRepo:()=>Vf,agentAvailability:()=>wc,answerFullscreenOffer:()=>tp,answerPrompt:()=>bc,archiveTrack:()=>_r,archiveTrackForMergedPr:()=>ze,augmentFirstPromptAfterHandoff:()=>hx,buildAgentCmd:()=>mx,canResumeSubtask:()=>Lx,cancelQueued:()=>ua,cancelQueuedToDraft:()=>Hf,cancelStopJob:()=>da,cancelSubtask:()=>Ic,changedRepos:()=>go,createSetupTrack:()=>Yf,createTrack:()=>Sc,deleteTrack:()=>Xf,ensureLeadEpochs:()=>ep,forceMergeSubtask:()=>ap,getHandoffJob:()=>No,getSubtaskJob:()=>fM,getTrack:()=>z,interruptLead:()=>kc,isArchiving:()=>sM,isHandoffNoteEcho:()=>Mf,isInterruptEcho:()=>Pf,isModelSwitchEcho:()=>Nf,listSubtaskJobs:()=>Ir,listTracks:()=>nt,matchTranscriptsToBoxes:()=>yx,mergeNotifyMessage:()=>Px,navigatePrompt:()=>Ff,notifyTrackForMergedPr:()=>Je,notifyTrackForResolvedPr:()=>vc,queueShipCompleteNotify:()=>Wf,queuedMessages:()=>Co,reapArchivedOrphans:()=>Qf,reconcileHandoffOutcome:()=>Dx,reconcileInterruptedHandoffs:()=>np,reconcileInterruptedSubtasks:()=>rp,reconcileSubtaskOutcome:()=>jx,refreshStandingInstructions:()=>Jf,rehydrateLead:()=>Tc,removeTrackRepo:()=>zf,reposOf:()=>fe,resolveTrackBox:()=>Ds,resolveTrackBoxByHookToken:()=>jf,restartLead:()=>Ac,resumeAgentCmd:()=>Bf,sayToLead:()=>de,sendOptionsForAgent:()=>_e,setTrackDraft:()=>Kf,setTrackModel:()=>Gf,startLeadHandoff:()=>Ec,startSpinUpView:()=>qf,startSubtask:()=>sp,stopJobActive:()=>vx,subtaskMessages:()=>Lf,trackMessages:()=>yc,unarchiveTrack:()=>Zf});import H from"node:fs/promises";import W from"node:path";import{randomUUID as aa}from"node:crypto";import{execFile as fC}from"node:child_process";import{promisify as pC}from"node:util";function mx(t,{skipPermissions:e=!0}={}){let n=hc[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 Bf(t,e,{skipPermissions:n=!0}={}){let r=hc[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 ca(t){let e=hc[t];if(e)return process.env[e.envVar]||e.base}async function wc({maxAgeMs:t=6e4}={}){if(Ln.value&&Date.now()-Ln.at<t)return Ln.value;let e=Ln.value,n=await Promise.all(ia.map(async r=>{let o=await Qa(ca(r),{retries:[]});return o===Hr?[r,e?.[r]??!0]:[r,o===Za]}));return Ln.value=Object.fromEntries(n),Ln.at=Date.now(),Ln.value}async function gc(t,e=!0){let n=mx(t,{skipPermissions:e});if(await Qa(n)===Ur){Ln.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.":Ll[r]?`Install it on this node: ${Ll[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}function hx(t,e=la){return`${t.trim()}
159
149
 
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 uO=/\(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"&&uO.test((t.text||"").trim())}var Ci=".bostrat/handoff/subtask-result.md",As=".bostrat/handoff/lead-context.md",dO=(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 ${As} \u2014 read that first for context. Your scoped task:
150
+ (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.)`}function Mf(t){return t.role==="user"&&yC.test((t.text||"").trim())}async function K(){return le||(Ef||=(async()=>{try{le=JSON.parse(await H.readFile(px,"utf8"))}catch{le={tracks:[]}}finally{Ef=null}return le})(),Ef)}async function Y(){await zt(px,le)}function xC(t,e){let n=JSON.stringify({trackId:t,...e})+`
151
+ `;return _f=_f.then(()=>H.mkdir(cn,{recursive:!0})).then(()=>H.appendFile(hC,n)).catch(()=>{}),_f}function wx(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=>SC.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 gx(t,e,n,{kind:r}={}){let o=await tt(e);if(!o)throw new Error(`box "${e}" is not running`);let s=ca(o.agent);await vn({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await Mt(e,n,{..._e(o.agent),kind:r||"orchestration"}),await G({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await K(),i=Q(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);mt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await Y()}async function vC(t){let e=W.join(hi(t),`${t.id}.ndjson`),n;try{n=await H.stat(e)}catch{return null}let r=lx.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await F(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return lx.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function TC(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:vd(n.name,n.agent)}async function ln(t,e=null){let n=e??new Map((await me()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=Er.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([vC(t),TC(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:ra.has(t.id)}}function yx(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-AC,f=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function bx(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(f=>f.box.sessionId),a=kh(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=yx(u,a,f=>i.has(f));for(let f of d){let p=u.find(m=>m.name===f.box);p&&(p.sessionId=f.sessionId),await dh(f.box,f.sessionId),n=!0}}return n}async function nt(){let t=await K();await bx(t)&&await Y();let e=new Map((await me()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>ln(n,e)))}async function Ds(t,e){let n=await K();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 jf(t){let e=zo(t);if(!e)return null;let n=await K();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&hm(o.hookTokenHash,e))return{track:r,box:o};return null}async function z(t){let e=await K(),n=Q(e,t);return n?(await bx(e)&&await Y(),ln(n)):null}function Pf(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(EC[t.agent]||[]).some(n=>n.test(e))}function Nf(t){let e=(t.text||"").trim();return e?_C.some(n=>n.test(e)):!1}async function yc(t,e={}){let n=await z(t);if(!n)return null;let r=new Set([...n.events.filter(g=>g.type==="orchestration-prompt"&&g.prompt).map(g=>String(g.prompt).trim()),...n.events.filter(g=>g.type==="said"&&g.text).map(g=>String(g.text).trim())]),o=No(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=qd(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Bb(n),...l],f={windows:[...Gw(n),...Vw(n,xi),...a,...u]},m=(await Promise.all(n.boxes.map(g=>qu(g,e)))).flat(),w=m.map(g=>{let E={kind:"message",...g};if(E.role==="assistant"&&(E.text||"").trim()&&E.tools?.length){let ft=As(E.agent).cliTools,st=E.tools.filter(Tt=>!ft.has(Tt));return{...E,tools:st.length?st:void 0}}return E}).map(g=>{if(g.role!=="user")return g;let E=yi(g.text);return E?E.kind==="suppress"?null:{...g,note:E,text:ww(E)}:g}).filter(Boolean).filter(g=>!(g.role==="user"&&r.has((g.text||"").trim()))).filter(g=>!Pf(g)).filter(g=>!Nf(g)).filter(g=>!Mf(g)).filter(g=>!Tf(g)).filter(g=>g.role!=="assistant"||!!(g.text||"").trim()||!!g.images?.length).filter(g=>!zw(g,f)),b=w.filter(g=>g.failed&&g.text===mu);if(b.length){let g=new Map;for(let E of b){if(!g.has(E.box)){let Tt=[...(await bt(E.box,{lines:Ct}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Gt=>Gt[1]);g.set(E.box,Tt.length?Tt[Tt.length-1]:null)}let ft=g.get(E.box);ft&&(E.text=`\u25A0 ${ft}`)}}let k=await Promise.all(n.boxes.map(g=>_g(g,e)));await bb(n,k.flat()),await gb(n,w),await Ty(n),await qb(n,w).catch(()=>{});for(let g of n.boxes)g.agent==="codex"&&g.state==="live"&&await xb(n,g),g.agent==="cursor"&&g.state==="live"&&await Eb(n,g);let S=await Promise.all(n.boxes.map(g=>Eg(g,e)));await yb(n,S.flat().filter(g=>!If(g.box,g.ts,u)));let $=await F(n),R=c&&i.viewId?i.viewId:null,J=Py($),O=ir($).filter(g=>!(g.kind==="message"&&(Pf(g)||Nf(g)||Mf(g)||Tf(g)))&&!(g.kind==="view-card"&&R&&g.viewId===R)),D=Ny(O,J),A=O.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&!D.has(g)&&If(g.box,g.ts,J))),P=_u($,A).map(g=>({kind:"command-batch",id:g.id,ts:g.pending?g.ts:(g.anchorTs??g.ts)-1,anchorTs:g.anchorTs,anchorBox:g.anchorBox,agent:g.agent,boxRole:g.boxRole,commands:g.commands,pending:g.pending})).filter(g=>!If(g.anchorBox,g.anchorTs??g.ts,u)),q=vw(Tw([...A,...P].filter(g=>g.ts!=null).sort((g,E)=>g.ts-E.ts))),at=RC(n),ot=at.size?q.filter(g=>g.kind==="event"||!at.has(g.box??g.anchorBox)):q,v=n.boxes.find(g=>g.name===n.leadBox)||null,M=v?await ds(v.cwd,v.sessionId,{fallbackNewest:!1}):null;if(!M&&v){let g=[...$].reverse().find(E=>E.type==="context.usage"&&E.boxId===v.name);g?.payload?.window&&(M={pct:g.payload.pct??(g.payload.occupancy!=null?g.payload.occupancy/g.payload.window:null),occupancy:g.payload.occupancy??null,window:g.payload.window,windowKnown:!0,model:g.payload.model||null})}let Rt=[...m].reverse().find(g=>g.role==="assistant"&&g.model),Kt=Rt?.model||null,yt=[...n.events||[]].reverse().find(g=>g.type==="model-change"),y=M?.model||Kt;yt&&(!Rt?.ts||yt.at>=Rt.ts)&&(y=yt.to);let h=v?await bt(v.name,{lines:Ct}).catch(()=>""):"",T=v&&ao(v.agent,h)?await xn(v.name).catch(()=>h):h,_=v?mr(v,T):null;if(_&&v){let g=Ei(v,_,T);if(g){let E=Wg(v.name,_.promptId);E.action!=="give-up"&&(E.action==="send"&&(vt(v.name,g.keys,{kind:"auto-reject"}).catch(()=>{}),E.first&&I(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:v.name,provider:v.agent,payload:{ts:Date.now(),promptId:_.promptId,rule:g.id}}).catch(()=>{})),_=null)}}!_&&v&&!ps(v.agent,h)&&!Co(n.id).length&&(_=Au(ot,{leadBoxId:v.name,provider:v.agent,isAnswered:g=>$.some(E=>E.type==="prompt.answered"&&E.payload?.promptId===g)})),_&&Ay(n,v,_);let wt=ot.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&g.ask&&!String(g.text||"").trim()&&!g.images?.length)),ut=!!v&&Ki(v.name,v.agent,h)&&!_;v&&!v.readyAt&&(ut||_||h&&li(v.agent,h))&&(v.readyAt=Date.now(),await Y());let Pr=!!v&&!v.readyAt&&Date.now()-(n.createdAt||0)<IC;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&v&&li(v.agent,h)&&!_){let g=await K(),E=Q(g,n.id);E?.setup&&!E.setup.connectedAt&&(E.setup.connectedAt=Date.now(),n.setup.connectedAt=E.setup.connectedAt,await I(E,{type:"provider.connected",actor:"system",source:"pane",boxId:v.name,provider:v.agent,payload:{provider:v.agent,ts:E.setup.connectedAt}}).catch(()=>{}),await Y())}let j=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,L=null;if(c){let g=Date.now()-i.startedAt;if(i.viewId&&!ut){let E=Qi(n.id);E&&(mt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:E.finishedAt}),await Y())}else if(_||g>Nb&&!ut&&!i.viewId||g>Wd){let E=_?"needs-input":g>Wd?"timeout":"no-view";Qi(n.id,{error:E})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:v?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:E}}).catch(()=>{})}else L={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:wt,context:M?{pct:M.pct,occupancy:M.occupancy,window:M.window,windowKnown:M.windowKnown,model:M.model}:null,models:{current:y,options:gy(v?.agent)},pendingPrompt:_,working:ut,stopping:vx(n.id),leadStarting:Pr,handoff:j,view:L,queued:Co(n.id).filter(g=>g.kind!=="ship-notify"&&g.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function RC(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 Lf(t,e){let n=await z(t);if(!n)return null;let r=Ir(t).find(d=>d.id===e)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await F(n),a=ir(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=_u(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await bt(o,{lines:Ct}).catch(()=>"");u=Ki(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function bc(t,{promptId:e,answers:n,text:r}={}){let o=Q(await K(),t);if(!o)throw new Error(`track "${t}" not found`);da(t,"prompt-answer");let s=o.boxes.find(f=>f.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 Xu(s);if(i||(i=await $C(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 f=typeof r=="string"?r.trim():"";if(!f)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(f)||f.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Mt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await vt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let f=kw(i,n||[],typeof r=="string"?r:"");if(!f)throw new Error("no answer selected \u2014 pick an option or reply in chat");await de(o.id,f,{kind:"prompt-answer"}),c={mode:"say",value:f,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let f=Lg(i,n||[]);await vt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=Mh(i,n||[]);await Mt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:f,count:1}}if(i.answerMode==="say"){let f=i.questions||[];for(let p=0;p<f.length;p++){let m=f[p],w=(Array.isArray(n?.[p])?n[p]:[]).map(b=>m.options?.[b]).filter(Boolean).map(b=>({label:b.label,description:b.description||""}));wd(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:p,questionTotal:i.questionTotal??f.length,header:m.header||"",question:m.question||"",multiSelect:!!m.multiSelect,chosen:w})}return await G({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(f=>l.options?.[f]).filter(Boolean).map(f=>({label:f.label,description:f.description||""})),d=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:u,...d?{control:d}:{}},await G({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),wd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function $C(t,e){if(!e)return null;let n=await F(t).catch(()=>[]);return Au(ir(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Ff(t,{promptId:e,questionIndex:n}={}){let r=Q(await K(),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 Xu(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=Fg(a,n);return c.length?(await vt(r.leadBox,c,{kind:"navigate-prompt"}),await G({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}}function Uf(t){let e=ta.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},ta.set(t,e)),e}function Co(t){return(ta.get(t)?.items??[]).map(e=>({...e}))}function ua(t){let e=ta.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 Hf(t){let e=ua(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=Q(await K(),t);if(o)return r=Mo(o,n),await Y(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function kx(t){let e=Uf(t);e.timer||(e.timer=setInterval(()=>CC(t).catch(()=>{}),OC))}async function CC(t){let e=ta.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Q(await K(),t);if(!n||n.archivedAt){ua(t);return}let r=No(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Ar(e,!1);return}let o=await tt(n.leadBox);if(!o){Ar(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(fo(s?.agent)){Ar(e,!1);return}let a=await bt(n.leadBox,{lines:Ct}).catch(()=>"");if(ci(s?.agent,a)){Ar(e,!1);return}if(ao(s?.agent,a)){let c=await xn(n.leadBox).catch(()=>a);if(mr(s,c)){Ar(e,!1);return}}if(!Ar(e,!0))return;let i=e.items.shift();e.idleSince=null,await Sx(n,s,o,i.text,i.kind??null,i.attachments??null),await Y()}finally{e.flushing=!1}}}async function xx(t,e,n,r){let o=fo(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Mo(t,[n]),await Y()),iy(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 Sx(t,e,n,r,o=null,s=null){await xx(t,e,r,o);let a=ea.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=hx(r,t.pendingHandoffHydrate.artifact||la),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(l.length){let d=ix(l.map(f=>f.rel));i=i.trim()?`${i.trim()}
161
152
 
162
- ${t}
153
+ ${d}`:d}let u=mt(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...l.length?{attachments:l}:{}});Oo(t,null);try{if(await vn({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:ca(n.agent)}),(ea.get(t.id)||0)!==a){!o&&r.trim()&&(Mo(t,[r]),await Y());let d=new Error("delivery stopped \u2014 the message was handed back as the draft");throw d.interrupted=!0,d}await Mt(t.leadBox,i,{..._e(e?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(d){throw mt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,...d?.interrupted?{reason:"interrupted"}:{}}),d}await VC(t,e,i)||(await Ax(t.leadBox,e?.agent,i.length+8).catch(()=>{}),mt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,reason:"unsubmitted"}),o||Mo(t,[r])),c&&await G({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function Wf(t,{actions:e}={}){Uf(t).items.push({id:aa(),text:Dy(e),ts:Date.now(),kind:"ship-notify"}),kx(t)}async function de(t,e,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(f=>f&&typeof f.rel=="string"):[];if((typeof e!="string"||!io(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Q(await K(),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(f=>f.name===s.leadBox),i=io(e);await xx(s,a,i,n),da(t,"say");let c=await tt(s.leadBox);if(!c&&await Tc(s)&&(await Y(),c=await tt(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Uf(t),u=await bt(s.leadBox,{lines:Ct}).catch(()=>""),d=l.items.length>0||ci(a?.agent,u);if(!d&&ao(a?.agent,u)){let f=await xn(s.leadBox).catch(()=>u);d=!!mr(a,f)}if(d){let f={id:aa(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(f),kx(t),Oo(s,null),await Y(),{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:l.items.length}}return await Sx(s,a,c,i,n,o),await Y(),{ok:!0,leadBox:s.leadBox}}async function qf(t,{hint:e}={}){let n=Q(await K(),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=qd(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";Db(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${ux}
163
154
 
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 ${As} \u2014 read that first for context. Your scoped task:
155
+ What to preview: ${String(e).trim()}`:ux;try{await de(t,a,{kind:"spin-up-view"})}catch(i){throw Qi(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function kc(t){let e=Q(await K(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);ea.set(t,(ea.get(t)||0)+1);let n=ua(t),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=$o.get(t);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Mo(e,r),await Y()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:e.draftRev||0}}let s=jC(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Fn(MC)]);let a=Q(await K(),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}}function vx(t){let e=$o.get(t);return!!e&&!e.canceled}function da(t,e){let n=$o.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function Tx(t){da(t,"track-ended"),ea.delete(t),ua(t)}function jC(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}),$o.set(t,r),(async()=>{let s=Q(await K(),t),a=s?.boxes.find(b=>b.name===s.leadBox),i=s?await tt(s.leadBox):null,c=!1,l=!1;if(i){t:for(let b=0;b<PC&&!r.canceled;b++){await vt(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of NC){await Fn(k);let S=await bt(s.leadBox,{lines:Ct}).catch(()=>"");if(!ps(a?.agent,S)){c=!0;break t}}}if(!c){l=!0;for(let b=0;b<BC&&!r.canceled&&(await Fn(DC),!r.canceled);b++){let k=await bt(s.leadBox,{lines:Ct}).catch(()=>"");if(!ps(a?.agent,k)){c=!0;break}await vt(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=Q(await K(),t)||s,d=u?.boxes.find(b=>b.name===u.leadBox)||a,f=null,p=null;u&&d&&i&&c&&!r.canceled&&(f=await HC(u,d).catch(()=>null),f||(p=await KC(u,d).catch(()=>null)));let m=u&&i&&!r.canceled?await FC(u).catch(()=>null):null,w=[f||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=Mo(u,w),await Y()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await LC(u||s,d,{ts:r.startedAt,result:r.result,dropped:e,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!f,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await G({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:t,sessionName:u?.leadBox,agent:d?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!f,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),$o.get(t)===r&&$o.delete(t),o()}),r}async function LC(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 FC(t){let e=af(await F(t));if(!e?.offerId)return null;let n=e.offerType==="exists";return await I(t,{type:n?"pr.exists.answered":"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,...n?{choice:"dismiss"}:{action:"dismiss",actions:null},reason:"interrupt"}}),e.offerId}async function HC(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 UC){await Fn(r);let o=await bt(t.leadBox,{lines:Ct}).catch(()=>""),s=ks(e.agent,o);if(s&&hu(n.text,s))return await Ax(t.leadBox,e.agent,n.text.length+8),mt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),n.text}return null}async function KC(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>WC||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!_t(r.text))return null;for(let s of qC){await Fn(s);let a;try{a=await qu(e,{})}catch{return null}if(cw(a,r))return null;let i=await bt(t.leadBox,{lines:Ct}).catch(()=>"");if(ks(e.agent,i))return null}return mt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function VC(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=Rf;r++){let o=!1;for(let s of GC){await Fn(s);let a=await bt(t.leadBox,{lines:Ct}).catch(()=>""),i=lw(e.agent,n,a,{busy:ps(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Rf&&(await vt(t.leadBox,["Enter"],{kind:"submit-retry"}),G({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return G({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:Rf,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function Ax(t,e,n){for(let r=0;r<3;r++){await Kr(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Fn(200);let o=await bt(t,{lines:Ct}).catch(()=>""),s=ks(e,o);if(!s)return!0;n=s.length+8}return!1}function Oo(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 Mo(t,e){let n=[],r=o=>n.some(s=>_t(s).includes(_t(o)));for(let o of[t.draft?.text,...e])o&&o.trim()&&!r(o)&&n.push(o);return n.length?Oo(t,n.join(`
165
156
 
166
- ${t}
157
+ `),{source:"restore"})?.text??null:null}function JC(t,e){let n=_t(e);if(!n)return!1;if(Co(t.id).some(o=>_t(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>zC)break;if(!(s.type!=="said"||!s.text||_t(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Kf(t,e,{rev:n=null}={}){if(typeof e!="string")throw new Error("text (string) required");let r=Q(await K(),t);if(!r)throw new Error(`track "${t}" not found`);let o=e,s=io(e);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&JC(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;_t(o).includes(_t(i))?Oo(r,o):Oo(r,`${i}
167
158
 
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.`,fO=(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,Ad=null;async function H(){return $e||(Ad||=(async()=>{try{$e=JSON.parse(await F.readFile(qb,"utf8"))}catch{$e={tracks:[]}}finally{Ad=null}return $e})(),Ad)}async function V(){await Ft(qb,$e)}var Z=(t,e)=>t.tracks.find(n=>n.id===e)||null,_d=Promise.resolve();function pO(t,e){let n=JSON.stringify({trackId:t,...e})+`
169
- `;return _d=_d.then(()=>F.mkdir(Qe,{recursive:!0})).then(()=>F.appendFile(sO,n)).catch(()=>{}),_d}var dt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),pO(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 Ed=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),mO=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=>mO.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=Ms(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 hO(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 wO(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Yu(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([hO(t),wO(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Ni.has(t.id)}}var gO=5e3;function yO(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-gO,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=yO(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=To(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 bO={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(bO[t.agent]||[]).some(n=>n.test(e))}var kO=[/^\/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?kO.some(n=>n.test(e)):!1}var xO=10*6e4;async function Pd(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=Cs(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=Ul(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=>Eu(b,e)))).flat(),h=m.map(b=>{let _={kind:"message",...b};if(_.role==="assistant"&&(_.text||"").trim()&&_.tools?.length){let lt=fs(_.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=>!Td(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===wl);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=>!Ed(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)||Td(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)&&Ed(b.box,b.ts,z))),N=_l(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=>!Ed(b.anchorBox,b.anchorTs??b.ts,u)),D=Cm(Nm([...S,...N].filter(b=>b.ts!=null).sort((b,_)=>b.ts-_.ts))),it=vO(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 Bo(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&&!Lo(T.agent,W)&&!Md(n.id).length&&(Nt=Al(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)<xO;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>Fl){let _=Nt?"needs-input":b>Fl?"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:OO(n.id),leadStarting:Ys,handoff:G,view:j,queued:Md(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function vO(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=mo(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=_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 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 Dd(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 Nu(s);if(i||(i=await SO(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||""}));Ju(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(()=>{}),Ju(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function SO(t,e){if(!e)return null;let n=await B(t).catch(()=>[]);return Al(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 Nu(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 TO=1500,_s=new Map;function Bd(t){let e=_s.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},_s.set(t,e)),e}function Md(t){return(_s.get(t)?.items??[]).map(e=>({...e}))}function Fi(t){let e=_s.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=Bd(t);e.timer||(e.timer=setInterval(()=>AO(t).catch(()=>{}),TO))}async function AO(t){let e=_s.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=Cs(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=Es.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=lO(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()}
159
+ ${o}`,{source:"restore"})}else Oo(r,o);return await Y(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Gf(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Q(await K(),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(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=yy(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await ds(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await vn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ca(o.agent)});let u=await by(a,r.leadBox,i);return mt(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await Y(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function Ex(t){for(let e of t){if(!e||!await _i(e))continue;let n=await Vg(e).catch(()=>null);if(!n)continue;if((await H.stat(W.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function fe(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:W.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function go(t){let e=[];for(let n of fe(t?.workspace)){if(!n?.dir)continue;let r=await od(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await sd(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function _x(t){let e=t;typeof e=="string"&&!W.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await Qo(e)??await kl(e));let n=await Ex([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 YC(t,e,n,r){let o=`track/${op(e)}-${t}`,s=[];try{await H.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await _x(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await rt(c).catch(()=>null),u=W.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${W.basename(c)}-${p}`;a.add(u);let d=W.join(n,u),f=await Ii(c);await co(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await xc(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await se(i.base,i.dir).catch(()=>{});throw await H.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Vf(t,e){let n=await K(),r=Q(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 _x(typeof e=="string"?e.trim():e);if((o.repos??[]).some(f=>f?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await rt(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(f=>f?.name).filter(Boolean)]),c=W.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let f=2;i.has(c);f++)c=`${W.basename(s)}-${f}`;let l=W.join(o.dir,c),u=await Ii(s);await zg(s,o.branch)?await Jg(s,l,o.branch):await co(s,l,o.branch,u);let d={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),d],await xc(o.dir,o.repos,o.branch),mt(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Ix(o)}),await Y(),d}async function zf(t,e){let n=await K(),r=Q(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 ue.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 od(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await sd(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 se(s.base,s.dir).catch(()=>{}),await H.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await xc(o.dir,o.repos,o.branch),mt(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Ix(o)}),await Y(),{removed:s.name}}async function XC(t,e,{repo:n,repos:r,cwd:o}={}){await H.mkdir(Cf,{recursive:!0});let s=W.join(Cf,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return YC(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!W.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Qo(n)??await kl(n));let i=await Ex([n,o,wC]);if(i){let c=`track/${op(e)}-${t}`;try{let l=await Ii(i);await co(i,s,c,l),await Df(s);let u=await rt(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await H.mkdir(s,{recursive:!0}),await Yg(s),await Df(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Df(t){try{await H.writeFile(W.join(t,"AGENTS.md"),Zs,{flag:"wx"})}catch{return}await eM(t,["/AGENTS.md"])}function tM(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
160
+ `);return`${QC}
170
161
 
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:Ms(n.agent)}),(Es.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 UO(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}={}){Bd(t).items.push({id:Os(),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 Ud(s)&&(await V(),c=await tt(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Bd(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:Os(),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=Ul(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}
162
+ This directory is NOT a git repo. It is a bostrat Track workspace holding one
163
+ git worktree per tagged repo, ALL on the same branch \`${e}\`:
172
164
 
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 jd(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`);Es.set(t,(Es.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=MO(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Mn(_O)]);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 _O=4e3,EO=3,IO=[300,350,400],RO=1e3,$O=12,co=new Map,Es=new Map;function OO(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"),Es.delete(t),Fi(t)}function MO(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<EO&&!r.canceled;y++){await At(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of IO){await Mn(k);let v=await mt(s.leadBox,{lines:_t}).catch(()=>"");if(!Lo(a?.agent,v)){c=!0;break t}}}if(!c){l=!0;for(let y=0;y<$O&&!r.canceled&&(await Mn(RO),!r.canceled);y++){let k=await mt(s.leadBox,{lines:_t}).catch(()=>"");if(!Lo(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 DO(u,f).catch(()=>null),d||(p=await LO(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await NO(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 CO(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 CO(t,e,n){await $(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}async function NO(t){let e=yd(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 PO=[250,400,500,600];async function DO(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 PO){await Mn(r);let o=await mt(t.leadBox,{lines:_t}).catch(()=>""),s=Ko(e.agent,o);if(s&&gl(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 BO=3e4,jO=[350,650,900];async function LO(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>BO||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!vt(r.text))return null;for(let s of jO){await Mn(s);let a;try{a=await Eu(e,{})}catch{return null}if(bm(a,r))return null;let i=await mt(t.leadBox,{lines:_t}).catch(()=>"");if(Ko(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 FO=[250,450,700],Id=2;async function UO(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=Id;r++){let o=!1;for(let s of FO){await Mn(s);let a=await mt(t.leadBox,{lines:_t}).catch(()=>""),i=km(e.agent,n,a,{busy:Lo(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Id&&(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:Id,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=Ko(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(`
165
+ ${n}
174
166
 
175
- `),{source:"restore"})?.text??null:null}var HO=12e4;function WO(t,e){let n=vt(e);if(!n)return!1;if(Md(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>HO)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&&WO(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}
167
+ cd into the repo a change belongs to before running git commands; commit (and
168
+ push / open PRs) in each repo separately, always on that shared branch. Work
169
+ only needs to land in the repos it actually touches \u2014 an untouched repo's
170
+ branch simply stays at its fork point and never gets pushed. When asked to run
171
+ or preview an app, run it from that app's own repo directory.
172
+ `}async function xc(t,e,n){let r=tM(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await H.writeFile(W.join(t,o),r).catch(()=>{})}async function $x(t){let{stdout:e}=await mC("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return W.resolve(t,e.trim())}async function eM(t,e){try{let n=await $x(t);await H.mkdir(W.dirname(n),{recursive:!0});let r=await H.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
173
+ `).includes(s));o.length&&await H.appendFile(n,(r.endsWith(`
174
+ `)||!r?"":`
175
+ `)+o.join(`
176
+ `)+`
177
+ `)}catch{}}async function nM(t,e){try{let n=await $x(t),r=await H.readFile(n,"utf8"),o=r.split(`
178
+ `).filter(s=>s!==e);o.length!==r.split(`
179
+ `).length&&await H.writeFile(n,o.join(`
180
+ `))}catch{}}async function Jf(){let t={refreshed:0,removed:0,codexGlobal:null};try{await H.mkdir(cn,{recursive:!0}),await H.writeFile(W.join(cn,"CLAUDE.md"),ZC).catch(()=>{}),await H.writeFile(W.join(cn,"AGENTS.md"),Zs).catch(()=>{});let e=process.env.CODEX_HOME||W.join(process.env.HOME||"/tmp",".codex");if((await H.stat(e).catch(()=>null))?.isDirectory()){let r=W.join(e,"AGENTS.md"),o=await H.readFile(r,"utf8").catch(()=>null);o===null?(await H.writeFile(r,Of,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):$f(o)&&o!==Of&&(await H.writeFile(r,Of).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Cf,Po]){let o=await H.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=W.join(r,s.name),i=W.join(a,"AGENTS.md"),c=await H.readFile(i,"utf8").catch(()=>null);c!==null&&$f(c)&&c!==Zs&&(await H.writeFile(i,Zs).catch(()=>{}),t.refreshed++);let l=W.join(a,"CLAUDE.md"),u=await H.readFile(l,"utf8").catch(()=>null);u!==null&&$f(u)&&(await H.rm(l,{force:!0}).catch(()=>{}),await nM(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function rM(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of fe(e))n.base&&n.dir&&await se(n.base,n.dir).catch(()=>{});await H.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await se(e.base,e.dir):await H.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Sc({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 gc(o,s),i=await K(),c=aa().slice(0,8),l=await XC(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(w=>w.name))),d=`${t.trim()} lead`;u.has(Fl(d))&&(d=`${t.trim().slice(0,50).trim()} ${c} lead`);let f=await rr(d,l.dir,{agentCmd:a,agent:o});ui({cwd:f.cwd,sessionName:f.name,agent:o,agentCmd:a});let p={id:c,name:t.trim(),cwd:f.cwd,workspace:l,leadBox:f.name,boxes:[Nx(f,"lead","live")],events:[],createdAt:Date.now()};return mt(p,"opened",{agent:o,box:f.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:w,baseRef:b,repo:k})=>({name:m,base:w,baseRef:b,repo:k}))}:{}}}),i.tracks.push(p),await Y(),ln(p)}async function Yf({provider:t="claude",fresh:e=!1}={}){if(!oM.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await K()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await ln(r);if(u.boxes.find(f=>f.name===u.leadBox)?.state==="live")return u}await _r(r.id).catch(()=>{})}let o=(await gc(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=aa().slice(0,8),a=W.join(cn,"setup",s);await H.mkdir(a,{recursive:!0});let i=await rr(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&ui({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:[Nx(i,"lead","live")],events:[],createdAt:Date.now()};return mt(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 K()).tracks.push(c),await Y(),ln(c)}async function Ox(t,e,n){await Wb(t).catch(()=>{});let r=oa.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),oa.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of Ir(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await ip(a).catch(()=>{}),ue.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 K(),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 Ee(a.name).catch(()=>{})}async function Xf(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await K(),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 Ak(s),Tx(t),await Ox(t,s,"track deleted"),n?await nx(t).catch(()=>{}):await ex(t).catch(()=>{}),await Sy(),await rM(s),e&&await Ek(t).catch(()=>{}),e?await Wk(t).catch(()=>{}):await Hk(s).catch(()=>{}),mt(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await Y(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}async function _r(t){let e=await K(),n=Q(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(),Tx(t),mt(n,"archived",{boxes:n.boxes.length}),await Y(),n.kind==="setup"?(await Cx(t),ln(n)):(ra.add(t),iM(t).catch(()=>{}).finally(()=>ra.delete(t)),{...await ln(n),archiving:!0})}async function iM(t){try{await cM(t)}catch{}await Cx(t).catch(()=>{})}async function Cx(t){let e=await K(),n=Q(e,t);if(!(!n||!n.archivedAt)){await Ox(t,n,"track archived");for(let r of n.boxes)Er.has(r.state)||(r.state="stopped");await Y()}}async function cM(t){let e=await K(),n=Q(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 qw(n.leadBox,i.cwd,{timeoutMs:aM})),s="lead-cli"}catch{}if(r||(r=ag(n,await F(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 Y()}async function ze(t,e){let n=await Ht(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Mx(n);if(r.length){for(let o of r)await gr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await _r(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Mx(t){return(await yr().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 Je(t,e){return vc(t,e,"merged")}async function vc(t,e,n="merged"){let r=await Ht(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 Mx(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await tb(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await de(r.trackId,Px(a),{kind:"merge-notify"}),!0}catch{return!1}}function Px(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 ${dx[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 ${dx[i.outcome]??i.outcome}`).join(`
181
+ `),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}
182
+ ${s}
183
+ ${a}`}async function Tc(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||Er.has(e.state)||await tt(e.name))return!1;let n=Bf(e.agent,e.sessionId),r=await Qa(n);if(r===Ur)return mt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===Hr&&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 rr(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=zo(o.hookToken),ui({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",mt(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(o){return mt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(o?.message||o)}),!1}}async function Zf(t){let e=await K(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,ra.delete(t),mt(n,"unarchived",{}),await Tc(n),await Y(),ln(n)}async function Qf(){let t=await K(),e=new Set((await me()).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 Ee(a.name).catch(()=>{}),mt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Er.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Y(),r}async function Ac(t){let e=await K(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(Er.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await tt(n.leadBox)){let a=await bt(n.leadBox,{lines:Ct}).catch(()=>"");if(ci(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ee(n.leadBox),r.state="stopped"}let s=await Tc(n);if(await Y(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return ln(n)}async function tp(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 F(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 Ac(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"}}function ep(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}async function mc(t,e){let n=await K(),r=Q(n,t);r&&(r.handoff={...r.handoff||{},...e},await Y())}function Dx(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 np(){let t=await K(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=Dx(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(),d=ep(l),f=d[d.length-1],p=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:la,at:u},mt(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await G({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 G({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ee(i).catch(()=>{}),a&&await Ee(a).catch(()=>{})}return e&&await Y(),e}async function Ec(t,e,n=!0,r=null){let o=oa.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Q(await K(),t);if(!s)throw new Error(`track "${t}" not found`);da(t,"handoff");let a=await gc(e,n),i=fo(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:wx(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return oa.set(t,l),await mc(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),lM(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 mc(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),G({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),G({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function lM(t,e,n,r,o){let s=async(R,J={})=>{t.step=R,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${R}`),await mc(t.trackId,{step:R,toBox:t.toBox}),await G({flow:"lead-handoff",phase:R,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 K(),i=Q(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",fo(c))t.checkpoint="skipped-capped";else try{await Xw(t.fromBox,{timeoutMs:gC})}catch(R){t.checkpoint="reconstructed",await G({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:t.trackId,fromBox:t.fromBox,error:R?.message||String(R)}).catch(()=>{})}t.checkpoint!=="llm"&&(t.statusArtifact=await Zw(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 f=await Nu(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await vn({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:ca(o)});let p=await bt(t.fromBox,{lines:Ct}).catch(()=>"");if(!li(o,p)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let R=Bf(c,l,{skipPermissions:t.skipPermissions});await Nu(t.fromBox,{agentCmd:R,agent:c,resumeSessionId:l,pinned:u});let J=await K(),O=Q(J,t.trackId);if(O){let D=O.boxes.find(A=>A.name===t.fromBox);D&&(D.state="live"),mt(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 Y()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let m=await K(),w=Q(m,t.trackId);if(!w)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let b=w.boxes.find(R=>R.name===t.fromBox);if(!b)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let k=Date.now(),S=ep(b),$=S[S.length-1];$.endedAt=k,t.reason&&($.endReason=t.reason),S.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:k,endedAt:null}),b.agent=o,b.sessionId=f.sessionId,b.pinned=f.pinned,b.state="live",b.role="lead",w.pendingHandoffHydrate={artifact:t.statusArtifact||la,at:k},mt(w,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:S.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt,checkpoint:t.checkpoint}),await Y(),await s("done",{sessionName:t.fromBox}),await mc(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}async function Bx(t,e,n){let r=await K(),o=Q(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await Y())}async function Ro(t,e){t.step=e,await fa(t)}function jx(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 Lx(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function pM(t,e,n){if(!Lx(n)||ue.has(e)||!await tt(n.box).catch(()=>null))return!1;try{await H.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 ue.set(e,s),Ux(s,o).catch(a=>Fx(s,a)),!0}async function rp(){let t=await K(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(ue.has(r))continue;if(await pM(n,r,o)){e=!0,mt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await G({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=jx(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Er.has(i.state)&&(i.state="canceled"),mt(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 G({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await fx(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Er.has(r.state)||ue.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?W.join(Po,`${n.id}-${r.subtaskId}`):null;await G({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await fx(n,r.name,o)}}return e=await mM(t)||e,e&&await Y(),e}async function fx(t,e,n){if(e&&await Ee(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of fe(t.workspace)){let s=W.join(n,o.name);await se(o.base,s).then(()=>!0,()=>!1)||await se(o.dir,s).catch(()=>{})}await H.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await tt(t.leadBox).catch(()=>null);r?.cwd&&await se(r.cwd,n).catch(()=>{})}async function mM(t){let e;try{e=await H.readdir(Po)}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 ue.values())o.dir&&n.add(W.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=W.join(Po,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of fe(a.workspace))await se(i.base,W.join(s,i.name)).catch(()=>{});await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await tt(a.leadBox).catch(()=>null):null;i?.cwd?await se(i.cwd,s).catch(()=>{}):await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await G({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function sp(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!io(n))throw new Error("task (string) required");let s=await gc(e,r),a=fo(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=Q(await K(),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=fe(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 _i(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 _i(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=aa().slice(0,8),f={id:d,trackId:t,trackName:i.name,agent:e,task:io(n),skipPermissions:r,reason:wx(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${op(i.name)}-${d}`,base:null,repos:u,dir:W.join(Po,`${t}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return ue.set(d,f),await fa(f),hM(f,c,s).catch(p=>Fx(f,p)),f}async function Fx(t,e){t.failedStep=t.step,t.error=e instanceof sa?"canceled":e?.message||String(e),t.step=e instanceof sa?"canceled":"error",t.finishedAt=Date.now(),await ip(t).catch(()=>{}),await fa(t).catch(()=>{}),await bM(t).catch(()=>{})}function Ic(t){let e=ue.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 ap(t){let e=ue.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 hM(t,e,n){if(await Ro(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await Es(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await fa(t)}else t.base=await Es(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await Ro(t,"spawn"),await H.mkdir(Po,{recursive:!0}),t.repos){await H.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await co(a.leadDir,W.join(t.dir,a.name),t.branch,a.base);let s=fe((Q(await K(),t.trackId)||{}).workspace);await xc(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 co(e.cwd,t.dir,t.branch,t.base),await Df(t.dir);await wM(t,e);let r=t.trackName.slice(0,38).trim(),o=await rr(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await gM(t,o),await gx(t.trackId,o.name,bC(t.task,!!t.repos),{kind:"subtask"}),await Ux(t,e)}async function Ux(t,e){await Ro(t,"working");let n=W.join(t.dir,pc);if(await xM(n,t),t.canceled)throw new sa;if(await Ro(t,"merge"),t.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of t.repos){let u=W.join(t.dir,l.name);await rd(u,`bostrat: subtask ${t.id} result (auto-committed)`),l.stat=await id(l.leadDir,l.base,t.branch,Io),l.files=await cd(l.leadDir,l.base,t.branch,Io);let d=await ld(l.leadDir,l.base,t.branch,Io);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(f=>W.join(l.name,f))),i.push(...l.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${l.name}: ${f}`:W.join(l.name,f))),d.applied||(c=!1)}t.stat=s,t.files=a,t.conflicts=i,t.applied=c}else{await rd(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await id(e.cwd,t.base,t.branch,Io),t.files=await cd(e.cwd,t.base,t.branch,Io);let s=await ld(e.cwd,t.base,t.branch,Io);t.applied=s.applied,t.conflicts=s.conflicts}await fa(t);let r=await kM(t,e,n);await Ro(t,"resume"),await gx(t.trackId,t.leadBox,kC(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await Ro(t,"reap"),await ip(t),t.step="done";let o=Date.now();await Bx(t.trackId,t.id,{..._c(t),finishedAt:o}),await yM(t,o),t.finishedAt=o}async function wM(t,e){let n=W.join(t.dir,W.dirname(Qs));await H.mkdir(n,{recursive:!0});let r="";try{r=await Sh(e.cwd,e.sessionId)}catch{}if(!r)try{r=await H.readFile(W.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}
184
+
185
+ **Delegated task:** ${t.task}
176
186
 
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 Bo(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:Ms(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 ys(t){let e=[];for(let n of qt(t?.workspace)){if(!n?.dir)continue;let r=await ju(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Lu(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 _o(e)??await _c(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 qO(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 ju(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Lu(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 KO(t,e,{repo:n,repos:r,cwd:o}={}){await F.mkdir(Od,{recursive:!0});let s=U.join(Od,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return qO(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await _o(n)??await _c(n));let i=await ck([n,o,aO]);if(i){let c=`track/${Wd(e)}-${t}`;try{let l=await ui(i);await Jr(i,s,c,l),await Cd(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 Cd(s),{dir:s,base:null,branch:null,kind:"repo"}}var Is="# bostrat workspace",pk=`${Is}
187
+ **Lead agent:** ${e.agent||"claude"} \xB7 **workspace:** ${e.cwd}
188
+
189
+ ---
190
+
191
+ ## Conversation so far (lead \u2192 you)
192
+
193
+ ${r}
194
+ `;await H.writeFile(W.join(t.dir,Qs),o)}async function gM(t,e){let n=await K(),r=Q(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:zo(e.hookToken),createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),mt(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]||{},..._c(t)},await Y())}async function yM(t,e=t.finishedAt){let n=await K(),r=Q(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),mt(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 Y()}async function bM(t){let e=await K(),n=Q(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),mt(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]||{},..._c(t)},await Y()}async function kM(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=W.join(e.cwd,r);await H.mkdir(W.dirname(o),{recursive:!0}),await H.copyFile(n,o)}catch{}return r}async function ip(t){if(t.box&&await Ee(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await se(e.leadDir,W.join(t.dir,e.name)).catch(()=>{});await H.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await tt(t.leadBox).catch(()=>null))?.cwd;n&&await se(n,t.dir).catch(()=>{})}}async function xM(t,e){let n=(e.startedAt||Date.now())+uM;for(;;){if(e.canceled||e.forceMerge)return;try{await H.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Fn(dM)}}function Fn(t){return new Promise(e=>setTimeout(e,t))}var mC,cn,px,hC,Cf,wC,hc,ia,Ln,la,gC,yC,pc,Qs,bC,kC,le,Ef,Q,_f,mt,If,SC,Er,lx,AC,EC,_C,IC,OC,ta,ux,MC,PC,NC,DC,BC,$o,ea,UC,WC,qC,GC,Rf,zC,Ix,na,Rx,ZC,Zs,$f,QC,Of,oM,ra,sM,aM,dx,Nx,oa,No,uM,dM,Po,Io,ue,fM,Ir,_c,fa,op,sa,br=C(()=>{Ut();Ou();ig();di();wn();Jl();dg();oe();He();Yo();oi();Ig();Qr();Zu();td();Kg();wu();ai();Eu();he();ud();Zn();ms();Ci();ky();gd();kb();Iu();xu();Sb();Nd();ct();ec();Vi();ho();cf();Ao();yf();fc();Af();Nn();cx();mC=pC(fC),cn=process.env.BOSTRAT_STATE_DIR||W.join(process.env.HOME||"/tmp",".bostrat"),px=W.join(cn,"tracks.json"),hC=W.join(cn,"events.jsonl"),Cf=W.join(cn,"workspaces"),wC=process.env.BOSTRAT_DEFAULT_REPO||null,hc={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"}},ia=Object.keys(hc);Ln={at:0,value:null};la=".bostrat/handoff/latest.md",gC=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;yC=/\(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\.\)$/;pc=".bostrat/handoff/subtask-result.md",Qs=".bostrat/handoff/lead-context.md",bC=(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 ${Qs} \u2014 read that first for context. Your scoped task:
195
+
196
+ ${t}
197
+
198
+ 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 ${pc} (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 ${Qs} \u2014 read that first for context. Your scoped task:
199
+
200
+ ${t}
201
+
202
+ 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 ${pc} 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.`,kC=(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."),le=null,Ef=null;Q=(t,e)=>t.tracks.find(n=>n.id===e)||null,_f=Promise.resolve();mt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),xC(t.id,r),vy(t,r),r};Eh(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await K()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(mt(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await Y().catch(()=>{}))});If=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),SC=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Er=new Set(["merged","conflict","canceled","failed","handed-off"]),lx=new Map;AC=5e3;EC={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};_C=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];IC=10*6e4;OC=1500,ta=new Map;ux="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.";MC=4e3,PC=3,NC=[300,350,400],DC=1e3,BC=12,$o=new Map,ea=new Map;UC=[250,400,500,600];WC=3e4,qC=[350,650,900];GC=[250,450,700],Rf=2;zC=12e4;Ix=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}))});na="# bostrat workspace",Rx=`${na}
178
203
 
179
204
  TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
180
205
  before substantive work \u2014 it is the shared cross-provider memory for this repo
@@ -299,6 +324,17 @@ under an earlier go-ahead, until the operator's decision on the card comes back
299
324
  to you as a chat message. A ticket ask mid-build means they want the tickets
300
325
  agreed BEFORE the build continues.
301
326
 
327
+ When asked to put this Track in a CIRCUIT (an initiative \u2014 "this belongs to the
328
+ launch circuit", "tag this under Ops"), run:
329
+
330
+ bostrat-circuit link <circuit-name-or-id>
331
+
332
+ A Track belongs to at most ONE circuit; link moves it, \`bostrat-circuit unlink\`
333
+ removes it, \`bostrat-circuit ls\` lists the circuits that exist. Circuits are
334
+ CREATED on the Circuits page, not from this box \u2014 if the name doesn't resolve,
335
+ report the near-matches the CLI prints and let the operator pick or create one;
336
+ never invent a circuit.
337
+
302
338
  When you FINISH a piece of work and steps remain that ONLY the operator can
303
339
  perform (vendor/app registrations, secrets to set, interactive logins, device or
304
340
  cert installs, DNS changes, subscriptions, physical steps), write those steps as
@@ -320,11 +356,24 @@ the SAME runbook \u2014 keep completed steps, struck through (~~like this~~ \u20
320
356
  add a near-duplicate. When every step is confirmed, run
321
357
  \`bostrat-runbook done <name-or-id>\`. At the START of related work, check
322
358
  \`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
359
+ than creating another. \`ls --all\` spans EVERY node, not just this one, and
360
+ \`bostrat-runbook show <name-or-id>\` prints a runbook's content \u2014 read before
361
+ deciding a fresh one is needed. A runbook marked \`[node:<name>]\` was authored
362
+ on another node: you can read it, but revising it needs a Track on that node \u2014
363
+ say so in chat rather than authoring a near-duplicate here. You are the only
364
+ writer of a runbook's CONTENT \u2014 the
324
365
  operator reads, acts, and reports back in chat. They can, however, mark one
325
366
  done (or reopen it) themselves from the Runbooks tab, so a runbook you find
326
367
  already \`done\` was closed out by them: don't reopen it to add a victory lap.
327
368
 
369
+ When asked about scheduled or routine jobs ("loops"), check \`bostrat-loop ls\` \u2014
370
+ it lists every Loop for this owner across ALL nodes (\`bostrat-loop show
371
+ <name-or-id>\` prints one loop's prompt, schedule and recent runs; a loop's run
372
+ Track is named "\u21BB <loop name>"). Loops are operator-managed from the Loops tab:
373
+ you can read them but never create, edit, fire, or delete one \u2014 describe any
374
+ proposed change in chat instead. Never claim a loop does or doesn't exist from
375
+ Track names or memory alone; \`bostrat-loop ls\` is the authority.
376
+
328
377
  When something happens that the operator would want to know about RIGHT NOW and
329
378
  they are plausibly away from the screen, you can reach their phone:
330
379
 
@@ -352,93 +401,69 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
352
401
  statement, or omitted entirely renders no controls \u2014 so close EVERY completed
353
402
  implementation turn this way, every time. When the operator has already asked you
354
403
  to commit / push / open the PR, just do it; do not re-offer.
355
- `,GO=pk,Ts=pk+Om;async function Cd(t){try{await F.writeFile(U.join(t,"AGENTS.md"),Ts,{flag:"wx"})}catch{return}await JO(t,["/AGENTS.md"])}var Rd=t=>typeof t=="string"&&(t===Is||t.startsWith(`${Is}
356
- `)),VO="# bostrat multi-repo workspace";function zO(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`${VO}
358
-
359
- This directory is NOT a git repo. It is a bostrat Track workspace holding one
360
- git worktree per tagged repo, ALL on the same branch \`${e}\`:
361
-
362
- ${n}
363
-
364
- cd into the repo a change belongs to before running git commands; commit (and
365
- push / open PRs) in each repo separately, always on that shared branch. Work
366
- only needs to land in the repos it actually touches \u2014 an untouched repo's
367
- branch simply stays at its fork point and never gets pushed. When asked to run
368
- or preview an app, run it from that app's own repo directory.
369
- `}async function Hi(t,e,n){let r=zO(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await F.writeFile(U.join(t,o),r).catch(()=>{})}var $d=Ts.replace(`${Is}
370
- `,`${Is}
404
+ `,ZC=Rx,Zs=Rx+xw;$f=t=>typeof t=="string"&&(t===na||t.startsWith(`${na}
405
+ `)),QC="# bostrat multi-repo workspace";Of=Zs.replace(`${na}
406
+ `,`${na}
371
407
 
372
408
  (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
373
409
  SCOPE: these instructions apply ONLY when your working directory is under
374
410
  ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
375
411
  When working anywhere else, ignore everything below.
376
- `);async function mk(t){let{stdout:e}=await oO("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function JO(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(`
378
- `)||!r?"":`
379
- `)+o.join(`
380
- `)+`
381
- `)}catch{}}async function XO(t,e){try{let n=await mk(t),r=await F.readFile(n,"utf8"),o=r.split(`
382
- `).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"),GO).catch(()=>{}),await F.writeFile(U.join(Qe,"AGENTS.md"),Ts).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,$d,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Rd(o)&&o!==$d&&(await F.writeFile(r,$d).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Od,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&&Rd(c)&&c!==Ts&&(await F.writeFile(i,Ts).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await F.readFile(l,"utf8").catch(()=>null);u!==null&&Rd(u)&&(await F.rm(l,{force:!0}).catch(()=>{}),await XO(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function YO(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 Ld({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=Os().slice(0,8),l=await KO(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(Kc(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 ZO=new Set(["claude","codex","cursor"]);async function wk({provider:t="claude",fresh:e=!1}={}){if(!ZO.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 po(r.id).catch(()=>{})}let o=(await ji(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Os().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=Rs.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Rs.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of mo(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 YO(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 po(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),tM(t).catch(()=>{}).finally(()=>Ni.delete(t)),{...await tn(n),archiving:!0})}var QO=9e4;async function tM(t){try{await eM(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 eM(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:QO})),s="lead-cli"}catch{}if(r||(r=gu(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=gu(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 po(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 Fd(t,e,"merged")}async function Fd(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,nM(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 nM(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(`
385
- `),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
- ${s}
387
- ${a}`}async function Ud(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=To(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 Ud(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 Ud(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:To(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 Rs=new Map,Cs=t=>Rs.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 rM(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=rM(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 Hd(t,e,n=!0,r=null){let o=Rs.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 Rs.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}),oM(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 oM(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:cO})}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 bu(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:Ms(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 bu(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 sM=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,aM=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,fo=U.join(Qe,"worktrees"),ao=[As,Ci],le=new Map;var mo=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)},Wi=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 Ik(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 Ns=t=>Ik(t.trackId,t.id,Wi(t));async function io(t,e){t.step=e,await Ns(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),Ck(s,o).catch(a=>Ok(s,a)),!0}async function Rk(){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",$s=class extends Error{};async function $k(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=Os().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 Ns(d),dM(d,c,s).catch(p=>Ok(d,p)),d}async function Ok(t,e){t.failedStep=t.step,t.error=e instanceof $s?"canceled":e?.message||String(e),t.step=e instanceof $s?"canceled":"error",t.finishedAt=Date.now(),await Kd(t).catch(()=>{}),await Ns(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 Mk(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 ps(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await Ns(t)}else t.base=await ps(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 Cd(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,dO(t.task,!!t.repos),{kind:"subtask"}),await Ck(t,e)}async function Ck(t,e){await io(t,"working");let n=U.join(t.dir,Ci);if(await gM(n,t),t.canceled)throw new $s;if(await io(t,"merge"),t.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of t.repos){let u=U.join(t.dir,l.name);await Bu(u,`bostrat: subtask ${t.id} result (auto-committed)`),l.stat=await Uu(l.leadDir,l.base,t.branch,ao),l.files=await Hu(l.leadDir,l.base,t.branch,ao);let f=await Wu(l.leadDir,l.base,t.branch,ao);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=>U.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:U.join(l.name,d))),f.applied||(c=!1)}t.stat=s,t.files=a,t.conflicts=i,t.applied=c}else{await Bu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Uu(e.cwd,t.base,t.branch,ao),t.files=await Hu(e.cwd,t.base,t.branch,ao);let s=await Wu(e.cwd,t.base,t.branch,ao);t.applied=s.applied,t.conflicts=s.conflicts}await Ns(t);let r=await wM(t,e,n);await io(t,"resume"),await Vb(t.trackId,t.leadBox,fO(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await io(t,"reap"),await Kd(t),t.step="done";let o=Date.now();await Ik(t.trackId,t.id,{...Wi(t),finishedAt:o}),await mM(t,o),t.finishedAt=o}async function fM(t,e){let n=U.join(t.dir,U.dirname(As));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}
388
-
389
- **Delegated task:** ${t.task}
390
-
391
- **Lead agent:** ${e.agent||"claude"} \xB7 **workspace:** ${e.cwd}
392
-
393
- ---
412
+ `);oM=new Set(["claude","codex","cursor"]);ra=new Set,sM=t=>ra.has(t);aM=9e4;dx={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Nx=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:zo(t.hookToken),createdAt:Date.now()});oa=new Map,No=t=>oa.get(t)||(le?Q(le,t)?.handoff:null)||null;uM=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,dM=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Po=W.join(cn,"worktrees"),Io=[Qs,pc],ue=new Map,fM=t=>ue.get(t)||(le?le.tracks.map(e=>e.subtasks?.[t]).find(Boolean):null)||null,Ir=t=>{let e=new Map([...ue.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=le?Q(le,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)},_c=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});fa=t=>Bx(t.trackId,t.id,_c(t));op=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",sa=class extends Error{}});Ut();import dt from"node:path";import Lt from"node:fs";import t1 from"node:readline";import{fileURLToPath as e1}from"node:url";import Se from"express";import n1 from"node:http";import r1 from"node:https";import dm from"node:os";import{WebSocketServer as o1}from"ws";Ut();import bA from"node-pty";async function hh(t,e,n={}){if(!kt(e)){ri(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await kn()){ri(t,`host tmux server not running on ${Qt}`),t.close(1011,"tmux server unavailable");return}if(!await tt(e)){ri(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=mh(n.cols,120,wh),o=mh(n.rows,32,gh),s;try{s=bA.spawn(N,["-S",Qt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ri(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Ul(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Ul(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"&&vA(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{}SA(e)})}var kA=6,xA=250;async function SA(t,e=kA,n=xA){for(let r=0;r<e;r++){if(await or(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var wh=20,gh=6;function vA(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=wh&&Number(e)>=gh}function mh(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Ul(t,e){try{t.send(JSON.stringify(e))}catch{}}function ri(t,e){Ul(t,{type:"error",message:e})}function yh(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}}he();oi();Ou();br();var SM=t=>!!t&&!t.finishedAt&&!t.canceled&&!t.error;function Wx({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(SM)&&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}}ct();oe();var vM=(...t)=>fetch(...t);function TM(t=process.env){return!!($t(t)&&t.BOSTRAT_PLANE_TOKEN)}async function cp(t=process.env){if(!TM(t))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let e=new AbortController,n=setTimeout(()=>e.abort(),1e4);try{let r=await vM(`${$t(t)}/v1/circuits`,{headers:{authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN}`},signal:e.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function lp(t,e=process.env){let n=String(t??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await cp(e),o=r.find(c=>c.id===n);if(o)return o;let s=n.toLowerCase(),a=r.filter(c=>c.name.toLowerCase()===s);if(a.length===1)return a[0];if(a.length>1)throw new Error(`"${n}" names ${a.length} circuits \u2014 use an id (bostrat-circuit ls)`);let i=r.filter(c=>c.name.toLowerCase().includes(s)).slice(0,5);throw new Error(i.length?`no circuit named "${n}" \u2014 near matches: ${i.map(c=>`${c.name} (${c.id})`).join(", ")}`:`no circuit named "${n}" \u2014 create it on the Circuits page first (bostrat-circuit ls lists yours)`)}async function Rc(t,e,{source:n="chat"}={}){return I(t,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:e?{circuitId:e.id,circuitName:e.name??null,source:n}:{circuitId:null,source:n}})}Af();ct();import ya from"node:fs/promises";import jc from"node:path";import mP from"node:os";wn();import AM from"node:fs/promises";import qx from"node:path";import EM from"node:os";var Kx=()=>qx.join(process.env.BOSTRAT_STATE_DIR||qx.join(process.env.HOME||EM.tmpdir(),".bostrat"),"integrations.json");async function un(){try{return JSON.parse(await AM.readFile(Kx(),"utf8"))||{}}catch{return{}}}var $c=["linked","prOpened","prMerged","create"],up=["prompt","auto","off"],_M={create:["prompt","off"]},Gx=t=>_M[t]??up;function Oc(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of $c){let s=Gx(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 Rr(){let t=await un(),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:Oc(t),defaultTeam:n}:null}async function Cc(){let t=await un(),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:Oc(t)}}async function dp(){return Oc(await un())}async function Vx(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${$c.join(", ")}`);for(let[r,o]of e){if(!$c.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${$c.join(", ")}`);let s=Gx(r);if(!s.includes(o))throw new Error(s.length<up.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${up.join(", ")}`)}let n=await un();return n.ticketFlow={...Oc(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await zt(Kx(),n,{mode:384}),n.ticketFlow}async function pa(){let t=(await un()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function IM(){let t=(await un()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var RM={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Mc(){let e=(await un()).voiceChat||{},n=e.provider||"openai",r=RM[n];if(!r)return null;let o=n==="openai"?await IM():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function zx(){let t=(await un()).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 $r(){let t=(await un()).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}}ct();yn();cr();Ao();import ha from"node:fs/promises";import r0 from"node:path";import tP from"node:os";import{randomUUID as o0}from"node:crypto";var pp={};dl(pp,{createComment:()=>jM,findIssueUrl:()=>MM,issueStates:()=>UM,mdToAdf:()=>Xx,moveIssue:()=>HM,parseIssueRef:()=>CM,resolveIssue:()=>PM,uploadImage:()=>LM,upsertCard:()=>NM,whoami:()=>fp});var Do=t=>String(t.baseUrl||"").replace(/\/$/,""),Yx=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Or(t,e,n,r){let o=await fetch(`${Do(t)}${n}`,{method:e,headers:{authorization:Yx(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 $M=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,OM=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function CM(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match($M);return r?r[1].toUpperCase():null}let n=e.match(OM);return n?n[1]:null}function MM(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 PM(t,e){let n;try{n=await Or(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:`${Do(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function NM(t,e,{url:n,title:r,subtitle:o}){await Or(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 DM=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function BM(t){let e=[],n=0;for(let r of t.matchAll(DM)){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 Xx(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
413
+ `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...BM(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 jM(t,e,n){let r=await Or(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Xx(n)});return r?.id?`${Do(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function fp(t){let e=await Or(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Do(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function LM(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(`${Do(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:Yx(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??`${Do(t)}/rest/api/3/attachment/content/${c.id}`}var FM={new:"unstarted",indeterminate:"started",done:"completed"},Jx=t=>FM[t?.key]??"started";async function UM(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([Or(t,"GET",`/rest/api/3/issue/${n}?fields=status`),Or(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Jx(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Jx(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function HM(t,e,n){await Or(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var gp={};dl(gp,{createComment:()=>YM,createIssue:()=>wp,createProject:()=>hp,findIssueUrl:()=>VM,issueStates:()=>ZM,moveIssue:()=>QM,parseIssueRef:()=>GM,resolveIssue:()=>zM,teams:()=>Bo,uploadImage:()=>XM,upsertCard:()=>JM,whoami:()=>mp});var WM=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function je(t,e,n={}){let r=await fetch(WM(),{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 qM=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,KM=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function GM(t){let e=String(t||"").trim(),n=e.match(qM);if(n)return n[1].toUpperCase();let r=e.match(KM);return r?r[1]:null}function VM(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 zM(t,e){let r=(await je(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 JM(t,e,{url:n,title:r,subtitle:o}){let s=await je(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 YM(t,e,n){let r=await je(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 mp(t){let e=await je(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 XM(t,{filename:e,contentType:n,buffer:r}){let o=await je(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
414
+ fileUpload(contentType: $contentType, filename: $filename, size: $size) {
415
+ success
416
+ uploadFile { uploadUrl assetUrl headers { key value } }
417
+ }
418
+ }`,{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 ZM(t,e){let n=await je(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 QM(t,e,n){if(!(await je(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 Bo(t){return((await je(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var Zx=255;async function hp(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>Zx,a=s?`${o.slice(0,Zx-1).trimEnd()}\u2026`:o,i=await je(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 wp(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await je(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 s0={linear:{api:gp,config:Rr},jira:{api:pp,config:Cc}};async function Tp(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(s0)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function Cr(t){let e=s0[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function Ap(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 a0=()=>process.env.BOSTRAT_STATE_DIR||r0.join(process.env.HOME||tP.tmpdir(),".bostrat"),xp=()=>r0.join(a0(),"tickets.json"),i0=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Pc=t=>`${i0()}/track/${t}`,Un=null,yp=null,bp=Promise.resolve();async function ga(){return Un||(yp??=ha.readFile(xp(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(Un=t,!e&&gt()){let{records:n,source:r}=await Ot("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;Un[s]=i}Object.keys(Un).length&&await Sp().catch(()=>{})}}return Un}).finally(()=>yp=null),yp)}async function Sp(){let t=JSON.stringify({tickets:Un},null,2),e=`${xp()}.${process.pid}.tmp`;await ha.mkdir(a0(),{recursive:!0}),await ha.writeFile(e,t),await ha.rename(e,xp())}async function Hn(t=null){if(bp=bp.then(Sp,Sp),await bp.catch(()=>{}),!t)return;let e=Un?.[t];e?Ft("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):pe("tickets",t).catch(()=>{})}async function Le(t){return(await ga())[t]??null}async function c0(){return{...await ga()}}async function l0(){return(await Tp()).length>0}async function jo(t,e,{source:n="api"}={}){let r=await Tp();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(e);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null,...t.workspace?.kind==="multi"?{repos:(t.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await ga())[t.id]=c,await Hn(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 wa(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 ma(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await f0(t.id,c,"linked"),c}async function Ep(t){let e=await ga();return e[t]?(delete e[t],await Hn(t),!0):!1}var vp=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function u0(t,e){if(!t.track?.cwd)return null;let n=In(await F({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function eP(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${vp(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${vp(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function nP(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(vp(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function wa(t,e){let n=await Cr(e);if(!n)return;let r=await u0(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:Pc(t),title:`Bostrat Track: ${e.track.name}`,subtitle:nP(e,r)})}function Nc(t){let e=new URL(i0());return`${e.protocol}//${t}.${e.host}/`}var rP={linked:"linked",opened:"prOpened",merged:"prMerged"};function oP(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 d0(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Hn(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 f0(t,e,n){let r=await Cr(e);if(!r)return;let o=r.cfg.ticketFlow?.[rP[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=Ap(s,a),c=oP(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 d0(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:o0(),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 p0(t){let e=await Le(t);if(!e)throw new Error("no ticket linked to this Track");let n=await Cr(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=Ap(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await Hn(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 _p(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await Le(t);if(!r)throw new Error("no ticket linked to this Track");let o=await Cr(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Ap(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 Hn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await d0(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function m0(t,e){let n=await Le(t);if(!n)throw new Error("no ticket linked to this Track");let r=await Cr(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 h0(t,e,n){if(!e)throw new Error("offerId required");let r=await F({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 _p(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 Qx=50;async function w0(){let t=await Rr();if(!t)throw new Error("no ticketing integration configured");return Bo(t)}function sP(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>Qx)throw new Error(`too many issues (${n.length}); cap is ${Qx} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function aP(t,e,n,r){let o=r??await Bo(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 Le(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 g0(t,e){let n=await Rr();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=sP(e),o=await Bo(n),s=await aP(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=o0();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 y0(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await F({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 Rr();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Bo(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let m=await hp(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let w=await wp(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}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 f=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&f.length===1&&!await Le(t.id)&&(p=await jo(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function ma(t,e){let n=await Cr(t);n&&await n.api.createComment(n.cfg,t,e)}async function iP(t,e,n){let r=await Cr(t);if(!r)return null;let o=await tn(e);if(!o)return null;let s=be(o,n);if(!s)return null;let a=await ha.readFile(ce(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 t0=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),kp=new Map;function cP(t,e){if(t0()<=0)return wa(t,e);if(kp.has(t))return;let n=setTimeout(()=>{kp.delete(t),Le(t).then(r=>r?wa(t,r):null).catch(()=>{})},t0());n.unref?.(),kp.set(t,n)}var e0=new Map;async function n0(t,e,n){let r=e0.get(t)??0;Date.now()-r<600*1e3||(e0.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 lP(t,{resolveTrack:e=null}={}){let n=await Le(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await Tp())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 jo(s,o,{source:"chat"})}catch(a){await n0(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 Hn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Hn(t.trackId),await wa(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 Hn(t.trackId),await wa(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await u0(n,t.trackId).catch(()=>null):null;await ma(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
394
419
 
395
- ## Conversation so far (lead \u2192 you)
420
+ Agent cost for this Track: ${eP(o)}.`:"")),await f0(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await iP(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await ma(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
396
421
 
397
- ${r}
398
- `;await F.writeFile(U.join(t.dir,As),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:To(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]||{},...Wi(t)},await V())}async function mM(t,e=t.finishedAt){let n=await H(),r=Z(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 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]||{},...Wi(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())+sM;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(aM)}}function Mn(t){return new Promise(e=>setTimeout(e,t))}import Ps from"node:fs/promises";import Gi from"node:path";import vM from"node:os";import yM from"ws";var Nk=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function qi(t,e,n={}){let r=await fetch(`${Nk()}/${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)=>qi("chat.postMessage",t,e),ho=(t,e)=>qi("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(`${Nk()}/${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 Pk(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 qi("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var kM=2200;function Dk({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 qi("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 Lk=()=>process.env.BOSTRAT_STATE_DIR||Gi.join(process.env.HOME||vM.tmpdir(),".bostrat"),Yd=()=>Gi.join(Lk(),"slack.json"),Ki=null,Vd=null,zd=Promise.resolve();async function Qd(){return Ki||(Vd??=Ps.readFile(Yd(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>Ki=t).finally(()=>Vd=null),Vd)}async function Bk(){let t=JSON.stringify({threads:Ki},null,2),e=`${Yd()}.${process.pid}.tmp`;await Ps.mkdir(Lk(),{recursive:!0}),await Ps.writeFile(e,t),await Ps.rename(e,Yd())}async function en(){zd=zd.then(Bk,Bk),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 Fk(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 ho(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:Fk(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 jk=new Set;function hr(t){jk.add(t),t.catch(()=>{}).finally(()=>jk.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 Uk(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?Gi.basename(c.base):"fresh workspace"),lead:"claude"},u=await Gd(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:Fk(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?Gi.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(`
422
+ ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await ma(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 ma(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Nc(r.slug)}
423
+ _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await cP(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await n0(t.trackId,s?.cwd??null,o)}}function b0({resolveTrack:t}){Yt.on("event",e=>{lP(e,{resolveTrack:t}).catch(()=>{})})}async function k0(){if(!gt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await ga(),r=0;for(let[o,s]of Object.entries(n))await Ft("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}ho();Ao();import uP from"ws";var x0=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Dc(t,e,n={}){let r=await fetch(`${x0()}/${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 Ip=(t,e)=>Dc("chat.postMessage",t,e),Mr=(t,e)=>Dc("chat.update",t,e);async function qt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function dP(t,e,n={}){let r=await fetch(`${x0()}/${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 S0(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await dP("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 Dc("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var fP=2200;function v0({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 Dc("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new uP(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let f=w=>{try{o.send(JSON.stringify(w?{envelope_id:d.envelope_id,payload:w}:{envelope_id:d.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,f()},fP);Promise.resolve().then(()=>e(d)).then(w=>{clearTimeout(m),!p&&(p=!0,f(d.accepts_response_payload?w:void 0))},w=>{clearTimeout(m),p||(p=!0,f()),n(`envelope handler failed: ${w?.message||w}`)})}),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 E0=()=>process.env.BOSTRAT_STATE_DIR||jc.join(process.env.HOME||mP.tmpdir(),".bostrat"),Mp=()=>jc.join(E0(),"slack.json"),Bc=null,Rp=null,$p=Promise.resolve();async function Np(){return Bc||(Rp??=ya.readFile(Mp(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>Bc=t).finally(()=>Rp=null),Rp)}async function T0(){let t=JSON.stringify({threads:Bc},null,2),e=`${Mp()}.${process.pid}.tmp`;await ya.mkdir(E0(),{recursive:!0}),await ya.writeFile(e,t),await ya.rename(e,Mp())}async function ke(){$p=$p.then(T0,T0),await $p.catch(()=>{})}async function Dp(t){return(await Np())[t]??null}async function hP(t,e){let n=await Np();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var X=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function ba(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function wP(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return ba(n.replace(/\n+/g," "),e)}function _0(t){let e=`*${X(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${X(t.repoLabel)} \xB7 \`${X(t.branch||"?")}\` \xB7 lead *${X(t.lead)}*`,r=t.kickoff?`> ${X(ba(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
424
+ ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Pc(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Pp(t,e){await Mr(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:_0(e)})}async function qn(t,e,n,r=void 0){return Ip(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var A0=new Set;function Wn(t){A0.add(t),t.catch(()=>{}).finally(()=>A0.delete(t))}var ht=t=>({response_type:"ephemeral",text:t}),gP="*/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._",yP=["claude","cursor","codex"];function Op(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 Cp=(t,e)=>ht(`No live Track matches \u201C${X(e)}\u201D. Live: ${t.map(n=>`\`${X(n.name)}\``).join(" \xB7 ")||"none"}`);async function I0(t,e,{name:n,repo:r,responseUrl:o}){let s=t.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=e.maxConcurrent){await qt(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 qt(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?jc.basename(c.base):"fresh workspace"),lead:"claude"},u=await Ip(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:_0(l),unfurl_links:!1});l.ts=u.ts,(await Np())[i.id]=l,await ke(),await qt(o,{...ht(`\u2713 *${X(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await qt(o,{...ht(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function bP(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Dp(n.id);s&&(s.archivedAt=Date.now(),await ke(),await Pp(e,s).catch(()=>{})),await qt(r,{...ht(`\u2713 *${X(n.name)}* archived.`),replace_original:!0})}catch(s){await qt(r,{...ht(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function kP(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await qt(o,{...ht(`\u2713 Handing *${X(n.name)}* to ${X(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await qt(o,{...ht(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function xP(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 qt(r,{...ht(`\u2713 Stopped *${X(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 qt(r,{...ht(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function SP(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:ba(`${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 *${X(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function vP(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?jc.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 *${X(s.name)}* \u2014 ${X(i)}${a.branch?` \`${X(a.branch)}\``:""} \xB7 lead ${X(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
400
425
  `)+(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(Uk(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 ho(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(Uk(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 ho(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 ho(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 ho(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 Pk(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 Hk(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=Dk({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?Jc(e.cwd):null}async function Wk(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 wo from"node:path";import Ji 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 qk(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 Kk(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 Vi=(()=>{try{return aC(import.meta.url)("../package.json").version||null}catch{return null}})(),iC=process.env.BOSTRAT_STATE_DIR||wo.join(process.env.HOME||Ji.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||Ji.homedir(),env:e=process.env}={}){let n=r=>{try{return oC.statSync(r),!0}catch{return!1}};return{claude:n(wo.join(t,".claude",".credentials.json")),codex:n(wo.join(e.CODEX_HOME||wo.join(t,".codex"),"auth.json"))}}var Rt=1,Gk=1e3,uC=3e4,dC=6e4,fC=1500,pC=500,zi=100,mC=2e3,hC=25e3,wC=6e4,gC=6e4;function Vk({url:t,token:e,nodeId:n=Ji.hostname(),name:r=Ji.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=Kk(t);let u=c.replace(/^http/,"ws"),f=new Map,d=new Map,p=wo.join(iC,"plane-link","cursor.json"),m={},h={},y=new Map,k=null,v=!1,I=!1,R=Gk,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(wo.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+=zi){let _=j.slice(b,b+zi).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+=zi){let bt=lt.slice(nt,nt+zi).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,...Cx}=Zt.headers;T({v:Rt,ch:"control",type:"http_head",reqId:_,status:Zt.statusCode||502,headers:Cx}),Zt.on("data",Nx=>{T({v:Rt,ch:"control",type:"http_data",reqId:_,data:Buffer.from(Nx).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:Vi,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=Gk,z=null,l.log(`[plane-link] connected to plane as ${n}`),b.minNodeVersion&&Vi&&cC(Vi,b.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${Vi}; 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 zk from"node:fs/promises";import js from"node:os";import bC from"node:path";var kC=1024*1024,Xi=t=>Math.round(t/kC);async function xC(t){try{let e=await zk.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:Xi(r),diskTotalMb:Xi(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 zk.readdir(bC.join(Bs,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function Jk({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?Xi(n-r):null,memTotalMb:Xi(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 Xk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Yk=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Yi=t=>Math.round(t*1e4)/1e4;async function Zk(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=Xk(),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);Yk(u,h.totalTokens);for(let[y,k]of Object.entries(h.byModel))n[y]||={usd:0,events:0,tokens:Xk()},n[y].usd=Yi(n[y].usd+k.usd),n[y].events+=k.events,Yk(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(h.byProvider))r[y]||={usd:0,events:0},r[y].usd=Yi(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:Yi(o),asRecordedUsd:Yi(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)}`,go=(t,e)=>String(t).padEnd(e);function Qk(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(` ${go(a,26)} ${go(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(` ${go(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(` ${go(c.label,40)} ${go(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))}${go(Cn(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
406
- `)}import IC from"node:fs/promises";import RC from"node:path";var Zi=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=>Zi(v)>=e,a=Gn([Fo(n?.name),Fo(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)=>Zi(v)-Zi(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=Zi(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 e0(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:t0(y.models),providers:t0(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},t0=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Nn(n)]));var yo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function $C(t){let e=String(t||"7d");return e in yo?{key:e,ms:yo[e]}:{key:"7d",ms:yo["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 n0(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,...e0(f,{windowMs:u,now:c})}}var Pn=36e5,a0=24*Pn,CC=400,NC=12,bo=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 yo?String(t||"7d"):"7d",i=yo[a];return{key:a,since:i==null?null:r-i,until:r}}var DC=(t,e)=>t!=null&&e-t<=48*Pn?Pn:a0;function r0(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 o0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),s0=(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,bo(n)]));async function i0(t,e={}){let n=e.now??Date.now(),r=PC({...e,now:n}),o=r.since==null?a0: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=o0(),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);s0(u,ct.totalTokens);for(let[T,g]of Object.entries(ct.byModel))c[T]||={usd:0,events:0,tokens:o0()},c[T].usd=bo(c[T].usd+g.usd),c[T].events+=g.events,s0(c[T].tokens,g.tokens);for(let[T,g]of Object.entries(ct.byProvider))l[T]||={usd:0,events:0},l[T].usd=bo(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=r0(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?r0(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:bo(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:bo(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:v.length,tracksSilentLive:R},prior:a!=null?{totalUsd:bo(h),since:r.since-a,until:r.since}:null,tracks:v.slice(0,50),boxes:I.slice(0,NC)}}import ko 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),c0=process.env.BOSTRAT_STATE_DIR||Ct.join(process.env.HOME||FC.tmpdir(),".bostrat"),Qi=!1,of=null;async function sf(t,e){let n=await ko.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await ko.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ko.mkdir(Ct.dirname(e),{recursive:!0}),await ko.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 tc(t,e,{skip:n=null}={}){let r=0,o=await ko.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 tc(a,Ct.join(e,s.name)):s.isFile()&&(r+=await sf(a,Ct.join(e,s.name))?1:0)}return r}async function l0({stateDir:t=c0,dest:e=process.env.BOSTRAT_SYNC_DIR||`${c0}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ko.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 tc(Ct.join(t,"events"),Ct.join(e,"events")),o+=await tc(Ct.join(t,"memory"),Ct.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await tc(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 Ac(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||Qi))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Qi&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Qi=!1,of=null}catch(u){Qi=!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 f0(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 ec=(t,e,n=15e3)=>VC("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),u0=new Map,d0=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 ec(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await ec(r,["remote","get-url","origin"])}catch{return d0.has(r)||(d0.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 ps(r,"bostrat: wip snapshot"),{stdout:s}=await ec(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(u0.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=XC(n);return await ec(r,["push","--force","origin",`${o}:${i}`],JC),u0.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 p0({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),nc=t=>new Promise(e=>setTimeout(e,t)),jn=Hc,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,m0=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 nc(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await nc(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",...Uc()));let e=`${Fc} --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 nc(400),e=await cf(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await nc(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 h0({fresh:t=!1}={}){return!t&&Ls&&Date.now()-Ls.at<m0?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()-(m0-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 w0(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:w0(i.primary),week:w0(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 g0(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 rc({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 rc({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function rc(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 h0({fresh:e})}}function bN(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var kN=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),oc=(t,e)=>Object.assign(new Error(t),{status:e});async function y0(t,e){if(!bN(e))throw oc("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Qt(e)}${t}`,{headers:kN(e)})}catch(o){throw oc(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw oc(r?.error||`plane replied ${n.status}`,n.status);return r}async function b0(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await y0(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function k0(t,{env:e=process.env}={}){if(!t)throw oc("message id required",400);return(await y0(`/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 x0(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 v0(t){let e=x0(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 S0(t,e){let n=x0(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 T0 from"node:crypto";import sc from"node:fs";import SN from"node:path";function A0(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=sc.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=T0.randomBytes(32).toString("base64url");try{sc.mkdirSync(e,{recursive:!0}),sc.writeFileSync(o,s+`
412
- `,{mode:384}),sc.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 _0(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:T0.timingSafeEqual(n,r)}function E0(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 I0({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=E0(o);return _0(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 R0(t,e,{enforce:n=!0}={}){return!n||_0(E0(t),e)?!0:Us(t)}import _N from"node:http";import EN from"node:net";function $0(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 ac(t){let e=$0(t.headers.host);return e?{slug:e,view:ql(e)}:null}var IN=t=>`<!doctype html><meta charset="utf-8">
426
+ _${o} archived_`:"")}async function TP(t,e){let n=await $r();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(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(Wn(I0(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),ht(`Starting *${X(u)}* on ${X(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:SP(u,i)}}case"archive":{if(!a.length)return ht("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Op(await r.listTracks(),a.join(" "));return i?(Wn(bP(t,n,{track:i,responseUrl:e.response_url})),ht(`Archiving *${X(i.name)}*\u2026`)):Cp(c,a.join(" "))}case"handoff":{let i=r.knownAgents??yP,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:d}=Op(await r.listTracks(),l);return u?(Wn(kP(t,n,{track:u,agent:c,responseUrl:e.response_url})),ht(`Handing *${X(u.name)}* to ${X(c)}\u2026`)):Cp(d,l)}case"stop":{if(!a.length)return ht("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Op(await r.listTracks(),a.join(" "));return i?(Wn(xP(t,n,{track:i,responseUrl:e.response_url})),ht(`Stopping *${X(i.name)}*\u2026`)):Cp(c,a.join(" "))}case"list":return ht(await vP(r));default:return ht(gP)}}async function AP(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Dp(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 ke(),await Mr(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${X(l||"question")}~
427
+ \u2713 *${X(a)}*`}}]}).catch(()=>{})}}catch(c){await qt(n.response_url,ht(`Couldn't answer: ${c?.message||c}`))}}async function EP(t,e,n,r){let{trackId:o,offerId:s,action:a}=r,i=t.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(f=>f.type==="pr.offered"&&f.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===s))throw new Error("offer already answered");let d=a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(d?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:d}))return;d&&await i.sayToLead(c.id,mo(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await qt(n.response_url,ht(`Couldn't act on the offer: ${c?.message||c}`))}}async function _P(t,e,n,r){let{trackId:o,offerId:s,choice:a}=r;try{let i=await t.deps.resolveTrack(o);if(!i)throw new Error("track not found");await t.deps.answerPrExists(i,{offerId:s,choice:a})}catch(i){await qt(n.response_url,ht(`Couldn't act on the offer: ${i?.message||i}`))}}async function IP(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await $r();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 qt(e.response_url,{...ht(`Starting *${X(s)}*\u2026`),replace_original:!0}),Wn(I0(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"?Wn(AP(t,r,e,o)):n.action_id==="bostrat-ship"?Wn(EP(t,r,e,o)):n.action_id==="bostrat-pr-exists"&&Wn(_P(t,r,e,o))}async function RP(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 hP(e.channel,e.thread_ts);if(!n)return;let r=await $r();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await qn(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function $P(t){return async e=>{if(e.type==="slash_commands")return TP(t,e.payload);if(e.type==="interactive"){await IP(t,e.payload);return}if(e.type==="events_api"){await RP(t,e.payload?.event);return}}}var OP=new Set(["turn.user","turn.agent","prompt.raised","prompt.answered","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","pr.state.changed","handoff.done","view.state.changed","mock.state.changed"]);function CP(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${X(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:ba(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 MP(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await Mr(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${X(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await qn(t,e,r.question||"The lead needs an answer",CP(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await ke()}async function PP(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 ke(),await Mr(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${X(s||"question")}~
428
+ \u2713 _answered_`}}]})}async function NP(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 qn(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${X(ba(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await ke()}async function DP(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await ke();let s=r.action==="dismiss"?"dismissed":r.action==="pr-exists"?`PR${r.existingPr?.number?` #${r.existingPr.number}`:""} already open \u2014 pick how to ship`:r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await Mr(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function BP(t,e,n){let r=n.payload||{};if(!r.offerId||e.offers?.[r.offerId])return;let o=r.pr?.number?`#${r.pr.number}`:"a PR",s=(i,c,l)=>({type:"button",action_id:"bostrat-pr-exists",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,choice:i}),...l?{style:l}:{}}),a=await qn(t,e,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${X(r.branch)})`:""} is already open. Push the update to it, or open a new PR?`}},{type:"actions",elements:[s("push",`Push to ${o}`,"primary"),s("new","Open a new PR"),s("dismiss","Not now")]}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await ke()}async function jP(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await ke();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await Mr(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function LP(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??FP)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await S0(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${X(o.name)}*${a}${o.group?` (group \`${X(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${X(a.name)}*`).join(", ");await qn(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${X(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function FP(t,e){let n=await tn(t);if(!n)return null;let r=be(n,e);if(!r)return null;let o=await ya.readFile(ce(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function UP(t,e){if(!OP.has(e.type))return;let n=await Dp(e.trackId);if(!n)return;let r=await $r();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 ke(),await Pp(r,n);return;case"turn.agent":{if(!o.text)return;let s=wP(o.text);if(!s)return;await qn(r,n,s,[{type:"section",text:{type:"mrkdwn",text:X(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Pc(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return MP(r,n,e);case"prompt.answered":return PP(r,n,e);case"pr.offered":return NP(r,n,e);case"pr.offer.answered":return DP(r,n,e);case"pr.exists.offered":return BP(r,n,e);case"pr.exists.answered":return jP(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 qn(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 ke(),await Pp(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await qn(r,n,`Live preview ${o.name}: ${Nc(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${X(o.name)}*: ${Nc(o.slug)}
429
+ _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return LP(r,n,e,t.deps)}}function R0(t){let e={deps:t};Yt.on("event",s=>{UP(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await $r()||(r&&clearInterval(r),r=null,n=v0({getToken:async()=>(await $r())?.appToken,onEnvelope:$P(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}he();ct();He();Id();vs();import HP from"node:fs/promises";function WP(t){let e=new Map;for(let n of String(t||"").split(`
430
+ `)){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 qP(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 KP=new Set(["Write","Edit","NotebookEdit"]),GP=220,VP=160;function zP(t,e){let n=t?.tool_name??null,r={tool:n},o=t?.tool_input;if(!o||typeof o!="object")return r;if(KP.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,GP)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=er(o.command.trim().replace(/\s+/g," ")).slice(0,VP));return r}function JP(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?We(e.cwd,e.sessionId):e?.cwd?ql(e.cwd):null}async function $0(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=zi(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:zP(t,o)});break;case"Stop":await YP(r,o,t,i);break;default:break}return a}async function YP(t,e,n,r){let o=[],s=JP(n,e);if(s)try{o=WP(await HP.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=qP(await F(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=d.model||"unknown",p=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=d.tokens[m];p.messageIds.push(d.messageId),u.set(f,p)}for(let[d,f]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:d,usd:_n(d,f.tokens),tokens:f.tokens,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}Yo();ct();Ci();import QP from"node:fs";import jp from"node:fs/promises";import tN from"node:http";import Lo from"node:path";import Uc from"node:os";import{createRequire as eN}from"node:module";import Lp from"ws";import XP from"node:path";import ZP from"node:os";var ka=process.env.BOSTRAT_STATE_DIR||XP.join(process.env.HOME||ZP.tmpdir(),".bostrat");function Bp(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 O0(t=process.env){if(!t.BOSTRAT_PLANE_URL)return null;let e=Bp(t);return e==="127.0.0.1"||e==="::1"||e==="localhost"?null:{host:e}}function C0(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 Lc=(()=>{try{return eN(import.meta.url)("../package.json").version||null}catch{return null}})(),nN=process.env.BOSTRAT_STATE_DIR||Lo.join(process.env.HOME||Uc.tmpdir(),".bostrat");function rN(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 oN({home:t=process.env.HOME||Uc.homedir(),env:e=process.env}={}){let n=r=>{try{return QP.statSync(r),!0}catch{return!1}};return{claude:n(Lo.join(t,".claude",".credentials.json")),codex:n(Lo.join(e.CODEX_HOME||Lo.join(t,".codex"),"auth.json"))}}var Nt=1,M0=1e3,sN=3e4,aN=6e4,iN=1500,cN=500,Fc=100,lN=2e3,uN=25e3,dN=6e4,fN=6e4;function P0({url:t,token:e,nodeId:n=Uc.hostname(),name:r=Uc.hostname(),listTracks:o,detectAgents:s=null,sampleMetrics:a=null,metricsMs:i=fN,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=C0(t);let u=c.replace(/^http/,"ws"),d=new Map,f=new Map,p=Lo.join(nN,"plane-link","cursor.json"),m={},w={},b=new Map,k=null,S=!1,$=!1,R=M0,J=null,O=null,D=null,A=null,P=null,q=!1;async function at(){try{m=JSON.parse(await jp.readFile(p,"utf8"))}catch{m={}}}function ot(){q=!0,!D&&(D=setTimeout(async()=>{if(D=null,!!q){q=!1;try{await jp.mkdir(Lo.dirname(p),{recursive:!0}),await jp.writeFile(p,JSON.stringify(m))}catch(j){l.error("[plane-link] cursor flush failed:",j?.message||j)}}},lN),D.unref?.())}function v(j){return!k||k.readyState!==Lp.OPEN?!1:(k.send(JSON.stringify(j)),!0)}function M(j){let L=Date.now();for(let{trackId:g,event:E}of j){let ft=b.get(E.id);b.set(E.id,{trackId:g,seq:E.seq,event:E,sentAt:L,tries:(ft?.tries??0)+1})}return v({v:Nt,ch:"events",type:"append",events:j})}function Rt(j){let L=null;for(let g of b.values())g.trackId===j&&(L==null||g.seq<L)&&(L=g.seq);return L}function Kt(j){let L=w[j];if(L==null)return;let g=Rt(j),E=g==null?L:Math.min(L,g-1);E>(m[j]??0)&&(m[j]=E,ot())}function yt(){if(!S)return;let j=Date.now(),L=[...b.values()].filter(g=>g.event&&j-g.sentAt>=dN);if(L.length){L.sort((g,E)=>g.trackId===E.trackId?g.seq-E.seq:g.trackId<E.trackId?-1:1),l.log(`[plane-link] re-sending ${L.length} unacked event(s) the plane never confirmed`);for(let g=0;g<L.length;g+=Fc){let E=L.slice(g,g+Fc).map(ft=>({trackId:ft.trackId,event:ft.event}));if(!M(E))return}}}async function y(){if(!a)return;let j;try{j=await a()}catch(L){l.error("[plane-link] metrics sample failed:",L?.message||L);return}j&&v({v:Nt,ch:"ctl",type:"metrics",metrics:j})}function h(){a&&(P&&clearInterval(P),y(),P=setInterval(y,i),P.unref?.())}async function T(){let j;try{j=await o()}catch(L){l.error("[plane-link] backfill listTracks failed:",L?.message||L);return}v({v:Nt,ch:"ctl",type:"tracks",ids:j.map(L=>L.id).filter(L=>typeof L=="string")});for(let L of j){if(!S)return;let g;try{g=await F(L)}catch{continue}let E=m[L.id]??0,ft=g.filter(st=>st.seq>E&&typeof st.id=="string");for(let st=0;st<ft.length;st+=Fc){let Tt=ft.slice(st,st+Fc).map(Gt=>({trackId:L.id,event:Gt}));if(!M(Tt))return}}}function _(j){!S||!j?.trackId||typeof j.id!="string"||M([{trackId:j.trackId,event:j}])}let wt=new Map;async function lt(j){let{reqId:L,method:g,path:E,headers:ft={},body:st}=j,Tt=Object.fromEntries(Object.entries(ft).filter(([,ve])=>ve!=null)),Gt=new AbortController;wt.set(L,Gt);try{if(j.host){await ut(j,Tt,Gt);return}let ve=await fetch(c+E,{method:g,headers:Tt,body:st?Buffer.from(st,"base64"):void 0,signal:Gt.signal}),fn=Object.fromEntries(ve.headers);for(let re of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete fn[re];if(fn["content-type"]||(fn["content-type"]="application/octet-stream"),v({v:Nt,ch:"control",type:"http_head",reqId:L,status:ve.status,headers:fn}),ve.body){for await(let re of ve.body)if(!v({v:Nt,ch:"control",type:"http_data",reqId:L,data:Buffer.from(re).toString("base64")}))break}v({v:Nt,ch:"control",type:"http_end",reqId:L,error:null})}finally{wt.delete(L)}}function ut(j,L,g){let{reqId:E,method:ft,path:st,body:Tt}=j,Gt=new URL(c);return new Promise(ve=>{let fn=tN.request({host:Gt.hostname,port:Gt.port,method:ft,path:st,headers:{...L,host:j.host},signal:g.signal},re=>{let{connection:h1,"keep-alive":w1,"transfer-encoding":g1,upgrade:y1,...Tv}=re.headers;v({v:Nt,ch:"control",type:"http_head",reqId:E,status:re.statusCode||502,headers:Tv}),re.on("data",Av=>{v({v:Nt,ch:"control",type:"http_data",reqId:E,data:Buffer.from(Av).toString("base64")})||re.destroy()}),re.on("end",()=>{v({v:Nt,ch:"control",type:"http_end",reqId:E,error:null}),ve()}),re.on("error",()=>{v({v:Nt,ch:"control",type:"http_end",reqId:E,error:"view upstream error"}),ve()})});fn.on("error",re=>{v({v:Nt,ch:"control",type:"http_end",reqId:E,error:re?.message||"view request failed"}),ve()}),Tt&&fn.write(Buffer.from(Tt,"base64")),fn.end()})}function Pr(){if($)return;k=new Lp(t,{headers:{authorization:`Bearer ${e}`}}),k.on("open",async()=>{let L=null;try{L=await s?.()||null}catch{L=null}v({v:Nt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Lc,providers:oN(),...L?{agents:L}:{}})}),k.on("message",L=>{let g;try{g=JSON.parse(L)}catch{return}if(g.ch==="ctl"&&g.type==="hello_ok"){S=!0,R=M0,J=null,l.log(`[plane-link] connected to plane as ${n}`),g.minNodeVersion&&Lc&&rN(Lc,g.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${Lc}; the plane asks for \u2265${g.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),A&&clearInterval(A),A=setInterval(()=>{v({v:Nt,ch:"ctl",type:"ping"}),yt()},uN),A.unref?.(),h(),T().catch(E=>l.error("[plane-link] backfill failed:",E?.message||E));return}if(g.ch==="ctl"&&g.type==="usage_caps"){Oi(g.caps??{},{source:"plane",updatedAt:g.updatedAt??Date.now()}).then(E=>l.log(`[plane-link] spend caps ${Object.keys(E.caps).length?`updated (${Object.keys(E.caps).join(", ")})`:"cleared"} from plane`)).catch(E=>l.error("[plane-link] spend-caps push rejected:",E?.message||E));return}if(g.ch==="events"&&g.type==="ack"&&Array.isArray(g.ids)){let E=new Set;for(let ft of g.ids){let st=b.get(ft);st&&(b.delete(ft),(w[st.trackId]??0)<st.seq&&(w[st.trackId]=st.seq),E.add(st.trackId))}for(let ft of E)Kt(ft);return}if(g.ch==="control"&&g.type==="http_open"){lt(g).catch(E=>v({v:Nt,ch:"control",type:"http_end",reqId:g.reqId,error:E?.message||"tunnel error"}));return}if(g.ch==="control"&&g.type==="http_cancel"){wt.get(g.reqId)?.abort();return}if(g.ch==="attach"&&g.type==="open"){let E=new Lp(u+g.path,Array.isArray(g.protocols)?g.protocols:[],g.host?{headers:{host:g.host}}:void 0);d.set(g.streamId,E),f.set(g.streamId,[]),E.on("open",()=>{let st=f.get(g.streamId)||[];f.delete(g.streamId);for(let{buf:Tt,bin:Gt}of st)E.send(Tt,{binary:Gt})}),E.on("message",(st,Tt)=>{let Gt=Tt?st:Buffer.from(String(st));v({v:Nt,ch:"attach",type:"data",streamId:g.streamId,data:Gt.toString("base64"),...Tt?{bin:!0}:{}})});let ft=()=>{f.delete(g.streamId),d.delete(g.streamId)&&v({v:Nt,ch:"attach",type:"close",streamId:g.streamId})};E.on("close",ft),E.on("error",ft);return}if(g.ch==="attach"&&g.type==="data"){let E=d.get(g.streamId);if(!E)return;let ft=Buffer.from(g.data,"base64"),st=g.bin===!0,Tt=f.get(g.streamId);Tt?Tt.push({buf:ft,bin:st}):E.send(ft,{binary:st});return}if(g.ch==="attach"&&g.type==="close"){let E=d.get(g.streamId);d.delete(g.streamId),f.delete(g.streamId),E?.close();return}});let j=()=>{S&&l.log("[plane-link] disconnected from plane"),S=!1,A&&(clearInterval(A),A=null),P&&(clearInterval(P),P=null),b.clear(),w={};for(let E of d.values())E.close();d.clear(),f.clear();for(let E of wt.values())E.abort();if(wt.clear(),$||O)return;J==null&&(J=Date.now());let L=Date.now()-J<aN,g=L?iN+Math.random()*cN:R;O=setTimeout(()=>{O=null,Pr()},g),O.unref?.(),L||(R=Math.min(R*2,sN))};k.on("close",j),k.on("error",j)}return Yt.on("event",_),at().then(Pr),{stop:()=>{$=!0,Yt.off("event",_),O&&clearTimeout(O),D&&clearTimeout(D),A&&clearInterval(A),P&&clearInterval(P),v({v:Nt,ch:"ctl",type:"bye",reason:"shutdown"}),k?.close()},status:()=>({connected:S,nodeId:n,cursor:{...m},inflight:b.size})}}import pN from"node:fs";import N0 from"node:fs/promises";import xa from"node:os";import mN from"node:path";uf();Ut();var hN=1024*1024,Hc=t=>Math.round(t/hN);async function wN(t){try{let e=await N0.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:Hc(r),diskTotalMb:Hc(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function gN(){try{return(await me()).filter(e=>e.isBostrat).length}catch{return null}}async function yN(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await N0.readdir(mN.join(ka,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function D0({stateDir:t=ka,now:e=Date.now}={}){let n=xa.totalmem(),[r,o,s,a]=await Promise.all([ic().catch(()=>null),wN(pN.existsSync(t)?t:xa.tmpdir()),gN(),yN()]),i=xa.cpus()?.length||null,c=xa.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?Hc(n-r):null,memTotalMb:Hc(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(xa.uptime())}}ct();nu();Ka();ct();cr();function Fp(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 B0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),j0=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Wc=t=>Math.round(t*1e4)/1e4;async function L0(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=B0(),d=0,f=[];for(let p of t||[]){let m=await F(p),w=In(m,{boxLabels:Fp(p)});if(w.turns===0){p.archivedAt?d++:f.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let b of w.unpricedModels||[])l.add(b);j0(u,w.totalTokens);for(let[b,k]of Object.entries(w.byModel))n[b]||={usd:0,events:0,tokens:B0()},n[b].usd=Wc(n[b].usd+k.usd),n[b].events+=k.events,j0(n[b].tokens,k.tokens);for(let[b,k]of Object.entries(w.byProvider))r[b]||={usd:0,events:0},r[b].usd=Wc(r[b].usd+k.usd),r[b].events+=k.events;e.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}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:Wc(o),asRecordedUsd:Wc(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:d+f.length,tracksSilentArchived:d,tracksSilentLive:f},tracks:e}}var Kn=t=>`$${t.toFixed(4)}`,Fo=(t,e)=>String(t).padEnd(e);function F0(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 ${Kn(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 ${Kn(e.asRecordedUsd)} \xB7 current-rates total ${Kn(e.totalUsd)} (${e.totalUsd>=e.asRecordedUsd?"+":""}${Kn(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(` ${Fo(a,26)} ${Fo(Kn(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(` ${Fo(a.name||a.id,30)} ${Kn(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Fo(c.label,40)} ${Fo(Kn(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${Fo(Kn(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
431
+ `)}Nd();Ci();ct();gd();import vN from"node:fs/promises";import TN from"node:path";eu();cr();var qc=t=>t.payload?.ts??t.ts,bN=12e4,Gn=t=>Math.round(t*1e4)/1e4,kN=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function xN(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>qc(S)>=e,a=sr([hs(n?.name),hs(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 $=sr(S.payload.task);$&&i.set(S.boxId,$)}let c=new Map,l=S=>{let $=S||"unattributed";return c.has($)||c.set($,{segments:[u(null)],lastTs:null,provider:null}),c.get($)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((S,$)=>qc(S)-qc($)),f=0,p=0,m=0,w={turns:0};for(let S of d){let $=l(S.boxId),R=$.segments[$.segments.length-1],J=qc(S);switch($.lastTs!=null&&J>$.lastTs&&s(S)&&(R.activeMs+=Math.min(J-$.lastTs,bN)),$.lastTs=J,S.provider&&($.provider=S.provider),S.type){case"turn.user":{let O=S.payload?.text;if(S.source==="hook"||!String(O||"").trim())break;let D=u(sr(O));$.segments.push(D),s(S)&&(D.turns+=1);break}case"peer.message":{let O=S.payload?.text;if(!String(O||"").trim())break;let D=u(sr(O));$.segments.push(D),s(S)&&(D.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let O=S.payload?.tool;O&&(R.tools[O]=(R.tools[O]||0)+1),s(S)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let{usd:O,tokens:D,model:A,unpriced:P}=qe(S);P&&(w.turns+=1),R.usd+=O,D&&(R.tokens.input+=D.input,R.tokens.output+=D.output,R.tokens.cacheRead+=D.cacheRead,R.tokens.cacheWrite+=D.cacheWrite),R.models[A]=(R.models[A]||0)+O,S.provider&&(R.provider=S.provider);break}case"handoff.done":s(S)&&(f+=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 b=new Map,k=!1;for(let[S,$]of c)for(let R of $.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let O=R.textCat?.category??i.get(S)?.category??jh(R.tools)?.category??a?.category??"uncategorized";b.has(O)||b.set(O,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let D=b.get(O);D.usd+=R.usd,D.activeMs+=R.activeMs,D.turns+=R.turns,D.toolCalls+=R.toolCalls;for(let[P,q]of Object.entries(R.models))D.models[P]=(D.models[P]||0)+q;let A=R.provider||$.provider;A&&(D.providers[A]=(D.providers[A]||0)+R.usd),A==="cursor"&&R.usd===0&&(k=!0)}return{perCategory:b,handoffs:f,merged:p,failed:m,sawCursorActivity:k,unpricedTurns:w.turns}}function H0(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 w of t||[]){let{track:b,events:k}=w;if(!k?.length){a+=1;continue}let{perCategory:S,handoffs:$,merged:R,failed:J,sawCursorActivity:O,unpricedTurns:D}=xN(w,{since:r});O&&(i=!0),c+=D;let A={trackId:b?.id,name:b?.name||b?.id,archived:!!b?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:$,subtasksMerged:R,subtasksFailed:J};for(let[P,q]of S){A.usd+=q.usd,A.activeMs+=q.activeMs,A.turns+=q.turns,A.categories[P]={usd:Gn(q.usd),activeMs:q.activeMs};for(let[v,M]of Object.entries(q.models))A.models[v]=Gn((A.models[v]||0)+M);for(let[v,M]of Object.entries(q.providers))A.providers[v]=Gn((A.providers[v]||0)+M);o.has(P)||o.set(P,kN());let at=o.get(P);at.usd+=q.usd,at.activeMs+=q.activeMs,at.turns+=q.turns,at.toolCalls+=q.toolCalls;for(let[v,M]of Object.entries(q.models))at.models[v]=(at.models[v]||0)+M;for(let[v,M]of Object.entries(q.providers))at.providers[v]=(at.providers[v]||0)+M;let ot=at.byTrack.get(b?.id)||{usd:0,activeMs:0};ot.usd+=q.usd,ot.activeMs+=q.activeMs,at.byTrack.set(b?.id,ot)}(A.usd>0||A.activeMs>0||A.turns>0)&&(A.usd=Gn(A.usd),A.dominantCategory=SN(A.categories),s.push(A))}let l=[...o.values()].reduce((w,b)=>w+b.usd,0),u=[...o.values()].reduce((w,b)=>w+b.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,p=new Map((t||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,b])=>({category:w,sharePct:f>0?Math.round((d==="usd"?b.usd:b.activeMs)/f*100):0,shareBasis:d,usd:Gn(b.usd),activeMs:b.activeMs,turns:b.turns,toolCalls:b.toolCalls,models:U0(b.models),providers:U0(b.providers),topWorkstreams:[...b.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:Gn(S.usd),activeMs:S.activeMs}))})).sort((w,b)=>b.usd-w.usd||b.activeMs-w.activeMs);return s.sort((w,b)=>b.usd-w.usd||b.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:Gn(l),activeMs:u,turns:[...o.values()].reduce((w,b)=>w+b.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var SN=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},U0=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Gn(n)]));var Uo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function AN(t){let e=String(t||"7d");return e in Uo?{key:e,ms:Uo[e]}:{key:"7d",ms:Uo["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=Ni(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 _N(t){if(!t?.cwd)return null;try{let n=(await vN.readFile(TN.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 W0(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}:AN(e),d=[];for(let f of t||[]){let p=await F(f),m=EN(f,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),d.push({track:f,events:m,brief:await _N(f)})}return{window:l,...H0(d,{windowMs:u,now:c})}}ct();cr();var Vn=36e5,V0=24*Vn,IN=400,RN=12,Ho=t=>Math.round(t*1e4)/1e4;function $N({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 Uo?String(t||"7d"):"7d",i=Uo[a];return{key:a,since:i==null?null:r-i,until:r}}var ON=(t,e)=>t!=null&&e-t<=48*Vn?Vn:V0;function q0(t,e){if(e===Vn)return Math.floor(t/Vn)*Vn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function CN(t,e){if(e===Vn)return t+Vn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var K0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),G0=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},MN=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Ho(n)]));async function z0(t,e={}){let n=e.now??Date.now(),r=$N({...e,now:n}),o=r.since==null?V0:ON(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=K0(),d=0,f=0,p=0,m=0,w=0,b=new Set,k=new Map,S=[],$=[],R=[],J=null;for(let A of t||[]){let q=(await F(A)).filter(v=>v.type==="cost.recorded");if(!q.length){A.archivedAt||R.push(A.name||A.id);continue}let at=[];for(let v of q){let M=v.ts??0;s(M)?at.push(v):i(M)&&(w+=qe(v).usd)}if(!at.length)continue;let ot=In(at,{boxLabels:Fp(A)});d+=ot.totalUsd,f+=ot.turns,p+=ot.repricedTurns||0,m+=ot.unpricedTurns||0;for(let v of ot.unpricedModels||[])b.add(v);G0(u,ot.totalTokens);for(let[v,M]of Object.entries(ot.byModel))c[v]||={usd:0,events:0,tokens:K0()},c[v].usd=Ho(c[v].usd+M.usd),c[v].events+=M.events,G0(c[v].tokens,M.tokens);for(let[v,M]of Object.entries(ot.byProvider))l[v]||={usd:0,events:0},l[v].usd=Ho(l[v].usd+M.usd),l[v].events+=M.events;for(let v of at){let{usd:M}=qe(v);if(!M)continue;let Rt=q0(v.ts??0,o),Kt=k.get(Rt)??{t:Rt,total:0,byProvider:{}};Kt.total+=M;let yt=v.provider||"unknown";Kt.byProvider[yt]=(Kt.byProvider[yt]||0)+M,k.set(Rt,Kt),(J==null||Rt<J)&&(J=Rt)}S.push({id:A.id,name:A.name||A.id,archived:!!A.archivedAt,usd:ot.totalUsd,turns:ot.turns,providers:Object.keys(ot.byProvider)});for(let[v,M]of Object.entries(ot.byBox))$.push({trackId:A.id,trackName:A.name||A.id,boxId:v,...M})}let O=r.since!=null?q0(r.since,o):J,D=[];for(let A=O,P=0;A!=null&&A<r.until&&P<IN;A=CN(A,o),P++){let q=k.get(A);D.push(q?{t:A,total:Ho(q.total),byProvider:MN(q.byProvider)}:{t:A,total:0,byProvider:{}})}return S.sort((A,P)=>P.usd-A.usd),$.sort((A,P)=>P.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:D,fleet:{totalUsd:Ho(d),turns:f,repricedTurns:p,unpricedTurns:m,unpricedModels:[...b],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:R},prior:a!=null?{totalUsd:Ho(w),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:$.slice(0,RN)}}oe();import Wo from"node:fs/promises";import PN from"node:fs";import jt from"node:path";import NN from"node:os";import{execFile as DN}from"node:child_process";import{promisify as BN}from"node:util";var jN=BN(DN),J0=process.env.BOSTRAT_STATE_DIR||jt.join(process.env.HOME||NN.tmpdir(),".bostrat"),Kc=!1,Up=null;async function Hp(t,e){let n=await Wo.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await Wo.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Wo.mkdir(jt.dirname(e),{recursive:!0}),await Wo.copyFile(t,e),!0)}var LN=new Set([".claude",".codex",".cursor",".credentials.json","auth.json",".claude.json","api-token","node.env","certs","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function Gc(t,e,{skip:n=null}={}){let r=0,o=await Wo.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(LN.has(s.name)||n?.has(s.name))continue;let a=jt.join(t,s.name);s.isDirectory()?r+=await Gc(a,jt.join(e,s.name)):s.isFile()&&(r+=await Hp(a,jt.join(e,s.name))?1:0)}return r}async function Y0({stateDir:t=J0,dest:e=process.env.BOSTRAT_SYNC_DIR||`${J0}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Wo.mkdir(e,{recursive:!0});let o=0;o+=await Hp(jt.join(t,"tracks.json"),jt.join(e,"tracks.json"))?1:0,o+=await Hp(jt.join(t,"events.jsonl"),jt.join(e,"events.jsonl"))?1:0,o+=await Gc(jt.join(t,"events"),jt.join(e,"events")),o+=await Gc(jt.join(t,"memory"),jt.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Gc(jt.join(u.cwd,".bostrat"),jt.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>jN("git",["-C",e,...u]);PN.existsSync(jt.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 bl(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||Kc))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Kc&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Kc=!1,Up=null}catch(u){Kc=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
432
+ `).slice(-3).join(" | ").slice(0,300);d!==Up&&(Up=d,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${d}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}ud();ct();import{execFile as FN}from"node:child_process";import{promisify as UN}from"node:util";var HN=UN(FN),WN=12e4,qN=6e4;function KN(t){return`refs/bostrat/wip/${t}`}function Q0(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function GN(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:WN}var Vc=(t,e,n=15e3)=>HN("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),X0=new Map,Z0=new Set;async function VN(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Vc(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Vc(r,["remote","get-url","origin"])}catch{return Z0.has(r)||(Z0.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 Es(r,"bostrat: wip snapshot"),{stdout:s}=await Vc(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(X0.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=KN(n);return await Vc(r,["push","--force","origin",`${o}:${i}`],qN),X0.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(`
433
+ `)[0]}`),{pushed:!1,reason:"push-failed"}}}function tS({listTracks:t,sweepMs:e=GN(),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 VN(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}}he();Qr();Ut();Xo();import{execFile as zN}from"node:child_process";import{promisify as JN}from"node:util";import{randomUUID as YN}from"node:crypto";import XN from"node:fs";import ZN from"node:os";import QN from"node:path";var Jn=JN(zN),zc=t=>new Promise(e=>setTimeout(e,t)),Yn=Bl,Wp=process.env.BOSTRAT_USAGE_PROBE_CWD||QN.join(ZN.homedir(),".bostrat","usage-probe"),tD=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,eD=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,nD=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,eS=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,zn=null,Sa=null,va=null;async function rD(){try{return await Jn(N,B("has-session","-t",Yn)),!0}catch{return!1}}function oD(){zn&&clearTimeout(zn),zn=setTimeout(()=>sD().catch(()=>{}),tD),zn.unref&&zn.unref()}async function sD(){zn&&(clearTimeout(zn),zn=null);try{await Jn(N,B("kill-session","-t",Yn))}catch{}}async function aD(){let t=Date.now()+nD;for(;;){let e=await qp().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await zc(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await zc(500)}}async function iD(){if(await rD())return;if(!await kn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");XN.mkdirSync(Wp,{recursive:!0}),await pn(Wp);let t=YN();await Jn(N,B("new-session","-d","-s",Yn,"-c",Wp,"-x","200","-y","60",...Dl()));let e=`${Nl} --session-id ${t}`;await Jn(N,B("send-keys","-t",Yn,e,"Enter")),await aD()}async function qp(){let{stdout:t}=await Jn(N,B("capture-pane","-t",Yn,"-p"));return t}async function cD(){await iD(),await Jn(N,B("send-keys","-t",Yn,"-l","/usage")),await Jn(N,B("send-keys","-t",Yn,"Enter"));let t=Date.now()+eD,e="";for(;await zc(400),e=await qp(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await zc(900),e=await qp(),await Jn(N,B("send-keys","-t",Yn,"Escape")).catch(()=>{}),oD(),e}function lD(t){let e=String(t||"").split(`
434
+ `).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 uD(t){let e=String(t||"").split(`
435
+ `).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 nS({fresh:t=!1}={}){return!t&&Sa&&Date.now()-Sa.at<eS?Sa.data:va||(va=(async()=>{try{let e=await cD(),n=lD(e),r={...n,contributors:uD(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Sa={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 Sa={at:Date.now()-(eS-3e3),data:n},n}finally{va=null}})(),va)}var dD=262144;function fD(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 rS(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:fD(t.resets_in_seconds)}:null}function pD(t){let e=String(t||"").split(`
436
+ `),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:rS(i.primary),week:rS(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 Kp(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await ar(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 Jt(n.file,dD),o=pD(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 oS(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 Jc({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 Kp(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Kp(null))}return n.has("cursor")&&r.push(await Jc({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Jc(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?Kp(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await nS({fresh:e})}}Zn();oe();oe();function mD(t=process.env){return!!($t(t)&&t.BOSTRAT_PLANE_TOKEN)}var hD=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),Yc=(t,e)=>Object.assign(new Error(t),{status:e});async function sS(t,e){if(!mD(e))throw Yc("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${$t(e)}${t}`,{headers:hD(e)})}catch(o){throw Yc(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Yc(r?.error||`plane replied ${n.status}`,n.status);return r}async function aS(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await sS(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function iS(t,{env:e=process.env}={}){if(!t)throw Yc("message id required",400);return(await sS(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}Rs();oc();function Gp(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 wD(t){let[e,n]=t.split("/"),r=Gp(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 cS(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(wD).filter(Boolean)}function gD(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function lS(t){let e=cS(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:gD,allowed(r){if(n)return!0;let o=Gp((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function uS(t,e){let n=cS(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Gp(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import dS from"node:crypto";import Xc from"node:fs";import yD from"node:path";function fS(t=process.env,{stateDir:e=ka,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=yD.join(e,"api-token");try{let a=Xc.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=dS.randomBytes(32).toString("base64url");try{Xc.mkdirSync(e,{recursive:!0}),Xc.writeFileSync(o,s+`
437
+ `,{mode:384}),Xc.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Ta(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 pS(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:dS.timingSafeEqual(n,r)}function mS(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 bD=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],kD=t=>bD.some(e=>e.test(t));function hS({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=mS(o);return pS(c,t)?a():kD(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||Ta(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 wS(t,e,{enforce:n=!0}={}){return!n||pS(mS(t),e)?!0:Ta(t)}He();ec();ec();import xD from"node:http";import SD from"node:net";function Zc(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 Qc(t){let e=Zc(t.headers.host);return e?{slug:e,view:zd(e)}:null}var vD=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;"),TD=t=>{let e=vD(t.name);return`<!doctype html><meta charset="utf-8">
413
438
  <meta http-equiv="refresh" content="3">
414
- <title>${t.name} \u2014 starting</title>
439
+ <title>${e} \u2014 starting</title>
415
440
  <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
416
441
  <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 O0(t,e,n){let r=ac(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 M0(t,e,n){let r=ac(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
442
+ <p><b>${e}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function gS(t,e,n){let r=Qc(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(TD(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"};delete a.authorization,delete a.cookie;let i=xD.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 yS(t,e,n){let r=Qc(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
443
  \r
419
444
  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
445
+ `),e.destroy(),!0;let a=SD.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"&&(l==="authorization"||l==="cookie"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
421
446
  `)+`\r
422
447
  \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 C0(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 $0(e)&&ql(n[0])?n.slice(1).join("."):e}var N0=10,P0=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 D0(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=ts(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>P0)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${P0}-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>N0)throw new Error(`${r.length} bullets \u2014 over the ${N0}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function B0(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 j0(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),L0=3e4,BN=30*6e4,jN=L0,LN=10*6e4,FN=10080*6e4,UN=60*6e4,HN=8,WN=1440*6e4,qN=3,F0="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ff=60*6e4,KN=15*6e4,ic=new Map,cc=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 U0={fast:jN,slow:LN,decay:UN};function H0(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 U0[H0(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",F0],{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",F0],{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 vs(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 xo(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 xo(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 xo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await xo(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 xo(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 xo(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 xo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await W0(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 W0(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=dd,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=Fd,batchThreshold:d=qN,detail:p=tP,merge:m=eP,updateBranch:h=nP,lookup:y=Bt,disarm:k=us,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-(ic.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=H0(T,e);S.set(K(T.repo,T.number),{lane:g,intervalMs:U0[g]}),ic.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=(cc.get(J)||0)+1;cc.set(J,at),at>=HN&&(await lP(E,D,it),cc.delete(J),ic.delete(J))}continue}cc.delete(J),W.state==="MERGED"||W.state==="CLOSED"?(W.state==="MERGED"?await W0(E,W,D,it):await cP(E,W,D,it),ic.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 q0(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await uP()}catch{}finally{t=!1}}},L0)}import nn from"node:fs/promises";import vo from"node:path";import Y0 from"node:os";import K0 from"node:fs/promises";import lc 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),V0=lc.dirname(pP(import.meta.url)),hP=lc.resolve(V0,"..","..","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 G0=new Map;async function xP(t){let e;try{e=await K0.stat(t)}catch{return null}let n=G0.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await K0.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:bP(r.mappings||"")};return G0.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:lc.resolve(V0,"..","..")}).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(lc.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 z0(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||vo.join(process.env.HOME||Y0.tmpdir(),".bostrat"),Ln=()=>vo.join(AP(),"telemetry"),Z0=()=>vo.join(Ln(),".relay-offsets.json"),_P=50,EP=14,IP=3e4,J0=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 Q0=t=>new Date(t).toISOString().slice(0,10),X0=0;async function tx(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 z0(s);await nn.mkdir(Ln(),{recursive:!0});let o=vo.join(Ln(),`${Q0(e)}.ndjson`);return await nn.appendFile(o,r.map(s=>JSON.stringify(s)+`
425
- `).join(""),"utf8"),e-X0>6*3600*1e3&&(X0=e,$P(e).catch(()=>{})),r.length}async function $P(t=Date.now()){let e=Q0(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(vo.join(Ln(),s),{force:!0});let o=await nx();for(let s of r)delete o[s];await rx(o)}var uc=new Map;function ex(t,e=Date.now()){let n=t||"unknown",r=uc.get(n);return r||(uc.size>512&&uc.clear(),r={tokens:40,at:e},uc.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 nx(){try{return JSON.parse(await nn.readFile(Z0(),"utf8"))||{}}catch{return{}}}async function rx(t){await nn.mkdir(Ln(),{recursive:!0}),await nn.writeFile(Z0(),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||Y0.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 nx(),o=0;for(let s of n){let a=vo.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+=J0){let p=f.slice(d,d+J0).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 rx(r)}return{sent:o,disabled:!1}}var hf=null,wf=!1;function ox(){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 fc from"node:fs/promises";import lx from"node:path";import CP from"node:os";var ux=()=>process.env.BOSTRAT_STATE_DIR||lx.join(process.env.HOME||CP.tmpdir(),".bostrat"),dx=()=>lx.join(ux(),"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"],pc=null,gf=null,yf=Promise.resolve();function sx(){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 fx(){return pc||(gf??=fc.readFile(dx(),"utf8").then(t=>JSON.parse(t)?.terms??sx()).catch(()=>sx()).then(t=>pc=t).finally(()=>gf=null),gf)}async function ax(){let t=JSON.stringify({terms:pc},null,2),e=dx(),n=`${e}.${process.pid}.tmp`;await fc.mkdir(ux(),{recursive:!0}),await fc.writeFile(n,t),await fc.rename(n,e)}function BP(){return yf=yf.then(ax,ax),yf.catch(()=>{})}async function bf(){return Object.values(await fx()).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 fx(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var ix=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),dc=t=>t.toLowerCase();function cx(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function px(t,e){let n=ix(t),r=ix(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]=dc(n[u])===dc(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;)dc(n[c])===dc(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(cx(r[l])&&i.add(r[l]),l++);for(;l<s;l++)cx(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 mx(){if(!Gs)return null;let t=await ns();if(!t||t.apiKey.slice(-6)!==Gs.keyTail)return null;let{keyTail:e,...n}=Gs;return n}async function hx(t,e){let n=await ns();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 wx(){let t=await Xa();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await KP()}:{configured:!1}}async function gx(){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 yx(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 Tx=ft.dirname(JP(import.meta.url)),bx=Number(process.env.PORT)||3362,mc=qk(process.env),Ax=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,So=v0(process.env.BOSTRAT_ALLOW_IPS),Af=S0(process.env.BOSTRAT_VIEW_FRONT_CIDRS,ac),_x=A0(),Ex=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=fe();x.disable("x-powered-by");x.use((t,e,n)=>{if(Us(t)||So.allowed(So.clientIp(t))||Af.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(O0);x.use(I0({token:_x,enforce:Ex}));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:Ax})});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 Bo(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 kx=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>kx){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,kx).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 Nd(),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 Zk(n);if(r.cursorUsage=id(),t.query.format==="text")return e.type("text/plain").send(Qk(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 n0(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 i0(await st(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=id(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=ms(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json(ms())});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 g0(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 Wk(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(!ex(So.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await tx(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 Io()).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 b0(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 k0(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 hc({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 Ix({repo:t=null,number:e=null,branch:n=null}){let r=await hc({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 hc({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(!xs.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 hc({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 vs(n,y),await Jt({repo:k,number:y,trackId:v.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await cs(k,y,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await rr(k,y,i===!0),await ls(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 ls(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 hc({repo:n,number:d.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await po(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 us(c,i).catch(()=>null):null;try{await md(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(!xs.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 ls(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 vs(m,p.number).catch(()=>{}),d.action="updated-armed"),!await cs(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 ls(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 us(a.repo,a.number).catch(()=>null),c=await fr(a.repo).catch(()=>null);c&&await md(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 ys(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 wd(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 wd(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 ys(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 Ix({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
448
+ `),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 bS(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}.${AD(n.host)}/`}function AD(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 Zc(e)&&zd(n[0])?n.slice(1).join("."):e}Ao();yf();fc();oe();var ED=1e4,_D=(...t)=>fetch(...t);function ID(t=process.env){return!!($t(t)&&t.BOSTRAT_PLANE_TOKEN)}async function kS({env:t=process.env}={}){if(!ID(t))return{loops:[],source:"unconfigured"};let e=new AbortController,n=setTimeout(()=>e.abort(),ED);try{let r=await _D(`${$t(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)}}yn();ct();Nn();fc();var xS=10,SS=200,RD=120,$D=24,OD=/^\d{4}-\d{2}-\d{2}$/;function Vp(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var CD=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function vS(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 MD=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function PD(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Js(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(`
449
+ `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,RD)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${$D}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=MD.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>SS)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${SS}-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>xS)throw new Error(`${r.length} bullets \u2014 over the ${xS}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function TS(t,{repos:e=Vp()}={}){let n=new Set(e);return(await yr()).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 AS(t,{md:e,day:n=null}){let r=PD(e),o=n==null||n===""?CD():String(n);if(!OD.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}}Ks();Nn();ct();Ks();oe();br();Nn();Rs();oc();import{execFile as ND}from"node:child_process";import{promisify as DD}from"node:util";var Jp=DD(ND),ES=3e4,BD=30*6e4,jD=ES,LD=10*6e4,FD=10080*6e4,UD=60*6e4,HD=8,WD=1440*6e4,qD=3,_S="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",zp=60*6e4,KD=15*6e4,tl=new Map,el=new Map,GD=/could not resolve|not found|no pull requests?/i,VD=t=>GD.test(String(t?.message||t||""));function zD(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<zp)}var IS={fast:jD,slow:LD,decay:UD};function RS(t,e){return t.armedAt&&e-t.armedAt<BD||zD(t,e)?"fast":e-(t.firstSeen||0)>FD?"decay":"slow"}function JD(t,e){return IS[RS(t,e)]}function Ia(t,e,n,r){let o=t.lanes?.get(V(e.repo,e.number))||{};return rc({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function YD(t,e){Ve("watch");try{let{stdout:n}=await Jp("gh",["pr","view",String(e),"--repo",t,"--json",_S],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Te({slug:t})}});return JSON.parse(n||"null")}catch(n){throw On(n)&&Cn(n),n}}async function XD(t){Ve("watch");try{let{stdout:e}=await Jp("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",_S],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Te({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw On(e)&&Cn(e),e}}async function ZD(){let t=new Map;for(let e of await nt().catch(()=>[]))t.set(e.id,e);return t}async function QD(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 tB(t,e){let n=await xr(t);return n?xo(n,e):null}async function eB(t,e,n){let r=await xr(t);if(!r)throw new Error(`no local checkout for ${t}`);return So(r,e,{method:n})}async function nB(t,e){let n=await xr(t);if(!n)throw new Error(`no local checkout for ${t}`);return qs(n,e)}async function rB(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 qo(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 oB=10*6e4;async function sB(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>WD){await qo(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 qo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await qo(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<oB)return;await qo(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 qo(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 qo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await $S(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Aa(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 aB(t,e){try{return await Jp("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ea(t,e,n,r=()=>!0){return(await F(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var _a=(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 iB(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 Ui(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Wi(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<KD;await Qy(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await Zy(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ea(a,"pushed",t.number,d=>d.payload?.sha===s)&&await Aa(a,"pushed",{..._a(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>zp)await Hi(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 Hi(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Ia(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Ea(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await Aa(a,"ci-fixed",{..._a(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Hi(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>zp)await qi(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await qi(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Ia(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Ea(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Aa(a,"reconciled",{..._a(t,e),base:i.baseRefName||null}).catch(()=>{}),await qi(t.repo,t.number).catch(()=>{})}}}}async function cB(t,e,n,r){let o=Ia(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Ea(s,"closed",t.number)&&await Aa(s,"closed",{..._a(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 lB(t,e,n){Ia(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Ea(r,"lost",t.number)&&await Aa(r,"lost",_a(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function $S(t,e,n,r,o={}){let s=Ia(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await F(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 uB(t={}){let{now:e=Date.now(),auth:n=Qd,view:r=YD,list:o=XD,links:s=eb,trackMap:a=ZD,emitMerged:i=QD,mark:c=Pn,archiveIfArmed:l=ze,notifyIfArmed:u=Je,notifyIfResolved:d=vc,batchThreshold:f=qD,detail:p=tB,merge:m=eB,updateBranch:w=nB,lookup:b=Ht,disarm:k=Ms,emitDisarmed:S=rB,localHasCommit:$=aB}=t;if(!(await n().catch(()=>null))?.authenticated||Li()>0)return 0;let O=(await s().catch(()=>[])).filter(v=>e-(tl.get(V(v.repo,v.number))||0)>=JD(v,e));if(!O.length)return 0;let D=Date.now(),A=new Map,P=new Map;for(let v of O){let M=RS(v,e);A.set(V(v.repo,v.number),{lane:M,intervalMs:IS[M]}),tl.set(V(v.repo,v.number),e),P.has(v.repo)||P.set(v.repo,[]),P.get(v.repo).push(v)}let q=await a().catch(()=>new Map),at={now:e,lanes:A,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,detail:p,merge:m,updateBranch:w,lookup:b,disarm:k,emitDisarmed:S,localHasCommit:$};for(let[v,M]of P){let Rt,Kt=new Set;if(M.length>f)Rt=await o(v).catch(()=>null);else{Rt=new Map;for(let yt of M)try{let y=await r(v,yt.number);y?.number!=null&&Rt.set(Number(y.number),y)}catch(y){VD(y)&&Kt.add(V(yt.repo,yt.number))}}if(Rt)for(let yt of M){let y=V(yt.repo,yt.number),h=Rt.get(Number(yt.number));if(!h){if(Kt.has(y)){let T=(el.get(y)||0)+1;el.set(y,T),T>=HD&&(await lB(yt,q,at),el.delete(y),tl.delete(y))}continue}el.delete(y),h.state==="MERGED"||h.state==="CLOSED"?(h.state==="MERGED"?await $S(yt,h,q,at):await cB(yt,h,q,at),tl.delete(y)):(await iB(yt,h,q,at).catch(()=>{}),yt.autoMerge&&await sB(yt,q,at).catch(()=>{}))}}let ot={};for(let{lane:v}of A.values())ot[v]=(ot[v]||0)+1;return console.log(`[pr-watch] probed ${O.length} link(s) (${Object.entries(ot).map(([v,M])=>`${v} ${M}`).join(", ")}) in ${Date.now()-D}ms`),O.length}function OS(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await uB()}catch{}finally{t=!1}}},ES)}Ut();ct();br();import{randomUUID as dB}from"node:crypto";var fB=2e4,CS=3,Ko=new Map,Yp=new Set;async function MS(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 pB(t={}){let{sessions:e=null,tracks:n=null,append:r=I,read:o=F,now:s=Date.now()}=t;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=e??await me(),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 d=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let f=c.get(d.name);if(f)if(l.add(d.name),f.alternateOn){let p=(Ko.get(d.name)||0)+1;Ko.set(d.name,p),p===CS&&(Yp.add(d.name),await MS(u,d.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:dB(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(Ko.get(d.name)||0)>=CS;if(Ko.delete(d.name),p||!Yp.has(d.name)){Yp.add(d.name);let m=await MS(u,d.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...Ko.keys()])l.has(u)||Ko.delete(u)}var Ra=null,Xp=!1;function PS(){return Ra?()=>{}:(Ra=setInterval(()=>{Xp||(Xp=!0,pB().catch(()=>{}).finally(()=>{Xp=!1}))},fB),Ra.unref?.(),()=>{clearInterval(Ra),Ra=null})}ho();cf();ct();Ks();ho();import{randomUUID as mB}from"node:crypto";async function hB(){let t=await Promise.resolve().then(()=>(br(),Hx));return{sayToLead:t.sayToLead,queueShipCompleteNotify:t.queueShipCompleteNotify}}var Zp={listRepoPrs:kr,readEvents:F,appendEvent:I,sayDeps:hB};async function wB(t,e=Zp){let n=t?.workspace?.branch;if(!n||!t?.cwd)return null;let r;try{r=await e.listRepoPrs(t.cwd,{caller:"pr-exists"})}catch{return null}let o=(r||[]).find(s=>s.headRefName===n);return o?{number:o.number,url:o.url??null,title:o.title??null}:null}async function Qp(t,e,{actions:n,notify:r=!1}={},o=Zp){let s=e?.payload||{};if(!Array.isArray(n)||!n.includes("pr")||s.toPr||Array.isArray(s.repos)&&s.repos.length||!s.branch)return null;let a=await wB(t,o);if(!a)return null;let i={id:t.id,cwd:t.cwd};await o.appendEvent(i,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.boxId,payload:{ts:Date.now(),offerId:s.offerId,action:"pr-exists",actions:n,...r?{notify:!0}:{},existingPr:{number:a.number}}});let c=mB();return await o.appendEvent(i,{type:"pr.exists.offered",actor:"system",source:"orchestrator",boxId:e.boxId,payload:{ts:Date.now(),offerId:c,srcOfferId:s.offerId??null,branch:s.branch,base:s.base??null,pr:a,actions:n,...r?{notify:!0}:{},offeredRebase:Array.isArray(s.offered)&&s.offered.includes("rebase")}}),{offerId:c,pr:a}}async function tm(t,{offerId:e,choice:n}={},r=Zp){if(!e)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(t),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===e);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===e))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=Di.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(t.id,mo(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(t.id,My(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:t.id,cwd:t.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:e,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(t.id,{actions:l})}return{say:c,choice:n}}import dn from"node:fs/promises";import Go from"node:path";import US from"node:os";import NS from"node:fs/promises";import nl from"node:path";import{execFile as gB}from"node:child_process";import{promisify as yB}from"node:util";import{fileURLToPath as bB}from"node:url";var kB=yB(gB),BS=nl.dirname(bB(import.meta.url)),xB=nl.resolve(BS,"..","..","client","dist"),SB="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",vB=new Map([...SB].map((t,e)=>[t,e]));function TB(t){let e=[],n=0,r=0;for(let o of t){let s=vB.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 AB(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=TB(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 EB(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 DS=new Map;async function _B(t){let e;try{e=await NS.stat(t)}catch{return null}let n=DS.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await NS.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:AB(r.mappings||"")};return DS.set(t,o),o}catch{return null}}var em=null;function IB(){return em||(em=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):kB("git",["rev-parse","--short","HEAD"],{cwd:nl.resolve(BS,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),em}var RB=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function $B(t,{distDir:e=xB}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(RB)){let[a,i,c,l]=s,u=await _B(nl.join(e,"assets",`${i}.map`)),d=u?EB(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function jS(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await IB();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await $B(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var OB=()=>process.env.BOSTRAT_STATE_DIR||Go.join(process.env.HOME||US.tmpdir(),".bostrat"),Xn=()=>Go.join(OB(),"telemetry"),HS=()=>Go.join(Xn(),".relay-offsets.json"),CB=50,MB=14,PB=3e4,LS=500,ne=(t,e)=>typeof t=="string"?t.slice(0,e):"";function NB(t,e){if(!t||typeof t!="object")return null;let n=ne(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:ne(t.anonId,64),sessionId:ne(t.sessionId,64),userId:ne(t.userId,64),release:ne(t.release,64),appVersion:ne(t.appVersion,32),route:ne(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:ne(t.error.kind,256),message:ne(t.error.message,8192),stack:ne(t.error.stack,32768),fingerprint:ne(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:ne(s?.n,64),r:ne(s?.r,128)}))),o}var WS=t=>new Date(t).toISOString().slice(0,10),FS=0;async function qS(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,CB):[]).map(s=>NB(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await jS(s);await dn.mkdir(Xn(),{recursive:!0});let o=Go.join(Xn(),`${WS(e)}.ndjson`);return await dn.appendFile(o,r.map(s=>JSON.stringify(s)+`
450
+ `).join(""),"utf8"),e-FS>6*3600*1e3&&(FS=e,DB(e).catch(()=>{})),r.length}async function DB(t=Date.now()){let e=WS(t-MB*24*3600*1e3),n=[];try{n=await dn.readdir(Xn())}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 dn.rm(Go.join(Xn(),s),{force:!0});let o=await GS();for(let s of r)delete o[s];await VS(o)}var rl=new Map;function KS(t,e=Date.now()){let n=t||"unknown",r=rl.get(n);return r||(rl.size>512&&rl.clear(),r={tokens:40,at:e},rl.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 GS(){try{return JSON.parse(await dn.readFile(HS(),"utf8"))||{}}catch{return{}}}async function VS(t){await dn.mkdir(Xn(),{recursive:!0}),await dn.writeFile(HS(),JSON.stringify(t),"utf8")}function BB(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||US.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 jB({fetchImpl:t=fetch}={}){let e=await zx();if(!e)return{sent:0,disabled:!0};let n;try{n=(await dn.readdir(Xn())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await GS(),o=0;for(let s of n){let a=Go.join(Xn(),s),i=r[s]||0,c;try{let f=await dn.open(a,"r");try{let p=(await f.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await f.read(c,0,c.length,i)}finally{await f.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
451
+ `).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=LS){let p=d.slice(f,f+LS).map(w=>BB(w)),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 VS(r)}return{sent:o,disabled:!1}}var nm=null,rm=!1;function zS(){nm||(nm=setInterval(async()=>{if(!rm){rm=!0;try{await jB()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{rm=!1}}},PB),nm.unref?.())}import sl from"node:fs/promises";import QS from"node:path";import LB from"node:os";var tv=()=>process.env.BOSTRAT_STATE_DIR||QS.join(process.env.HOME||LB.tmpdir(),".bostrat"),ev=()=>QS.join(tv(),"dictation-glossary.json"),FB=100,UB=500,HB=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],al=null,om=null,sm=Promise.resolve();function JS(){let t={},e=Date.now();for(let n of HB)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function nv(){return al||(om??=sl.readFile(ev(),"utf8").then(t=>JSON.parse(t)?.terms??JS()).catch(()=>JS()).then(t=>al=t).finally(()=>om=null),om)}async function YS(){let t=JSON.stringify({terms:al},null,2),e=ev(),n=`${e}.${process.pid}.tmp`;await sl.mkdir(tv(),{recursive:!0}),await sl.writeFile(n,t),await sl.rename(n,e)}function WB(){return sm=sm.then(YS,YS),sm.catch(()=>{})}async function am(){return Object.values(await nv()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var qB=t=>Math.max(1,Math.ceil(t.length/4));async function KB(){let t=[],e=0;for(let{term:n}of await am()){let r=qB(n);if(!(e+r>UB)&&(t.push(n),e+=r,t.length>=FB))break}return t}async function GB(t,e){let n=await nv(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var XS=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),ol=t=>t.toLowerCase();function ZS(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function rv(t,e){let n=XS(t),r=XS(e);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=ol(n[u])===ol(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)ol(n[c])===ol(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(ZS(r[l])&&i.add(r[l]),l++);for(;l<s;l++)ZS(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await GB(u,"learned");return await WB(),[...i]}var $a=null;function VB(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 ov(){if(!$a)return null;let t=await pa();if(!t||t.apiKey.slice(-6)!==$a.keyTail)return null;let{keyTail:e,...n}=$a;return n}async function sv(t,e){let n=await pa();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 KB())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=VB(o.status,i);c.persistent&&($a={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 $a=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ct();var im={"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 zB(t){if(!t)return null;let e=String(t).trim();if(im[e])return e;let n=null;for(let r of Object.keys(im))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function JB(t,e={}){let n=im[zB(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 YB(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 Oa=null;async function XB(){if(!Oa)return null;let t=await Mc();if(!t||t.apiKey.slice(-6)!==Oa.keyTail)return null;let{keyTail:e,...n}=Oa;return n}async function ZB(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=YB(e.status,o);s.persistent&&(Oa={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}Oa=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 QB={openai:ZB};async function av(){let t=await Mc();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await XB()}:{configured:!1}}async function iv(){let t=await Mc();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 QB[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function cv(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=JB(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 mv=dt.dirname(e1(import.meta.url)),lv=Number(process.env.PORT)||3362,il=Bp(process.env),hv=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Vo=lS(process.env.BOSTRAT_ALLOW_IPS),fm=uS(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Qc),wv=fS(),gv=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=Se();x.disable("x-powered-by");x.use((t,e,n)=>{e.setHeader("x-content-type-options","nosniff"),Zc(t.headers.host)||(e.setHeader("x-frame-options","DENY"),e.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});x.use((t,e,n)=>{if(Ta(t)||Vo.allowed(Vo.clientIp(t))||fm.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(gS);x.use(hS({token:wv,enforce:gv}));x.use("/api/mocks",Se.json({limit:"1mb"}));x.use("/api/mocks",Se.raw({type:"image/png",limit:"24mb"}));x.use("/api/mocks",Se.raw({type:["video/mp4","video/webm"],limit:"105mb"}));x.use("/api/runbooks",Se.json({limit:"1mb"}));x.use("/api/telemetry",Se.json({limit:"256kb"}));x.use("/api/hook",Se.json({limit:"2mb"}));x.use("/api/dictation",Se.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",Se.raw({type:()=>!0,limit:"25mb"}));x.use(Se.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Qt,hostTmuxAlive:await kn(),autoTriggerPct:hv})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await me()})});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 rr(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(!kt(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 Ee(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(!kt(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 ds(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(!kt(n))return e.status(400).json({error:"invalid session name"});try{let r=await Jw(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=xi(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 uv=10;x.get("/api/idle-guard",async(t,e)=>{try{e.json(Wx({tracks:await nt(),views:await Vd(),queuedOf:n=>Co(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>uv){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,uv).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:ia,agentAvailability:await wc(),tickets:await c0()})});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 L0(n);if(r.cursorUsage=Pd(),t.query.format==="text")return e.type("text/plain").send(F0(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 W0(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 z0(await nt(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=Pd(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=_s(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json(_s())});x.post("/api/usage/caps",async(t,e)=>{try{e.json(await Oi(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 oS(await nt(),{fresh:n}))}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 wm(t.get(Na),{byToken:jf,legacy:()=>Ds(t.body?.session_id,t.body?.cwd)});if(n==="refused")return Jo("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Jo("legacy-denied",{reason:"tokenless-denied",hook:t.body?.hook_event_name}),e.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&t.body?.session_id&&r.box?.sessionId&&t.body.session_id!==r.box.sessionId&&Jo("mismatch",{track:r.track.id,box:r.box.name}),Jo(n,{track:r?.track?.id,hook:t.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await $0(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(!KS(Vo.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await qS(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 Fe(),r=new Set((await Promise.all(n.map(a=>rt(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await es()).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:Fa(),calls:Ky(),latency:zb()})});x.post("/api/github/refresh",(t,e)=>{Em(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await aS(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 iS(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function pm(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 cl({repo:t=null,number:e=null,branch:n=null}){let r=t?await rt(t):null;if(r&&e!=null){let s=await Ht(r,e),a=s&&await z(s.trackId);if(a)return a}return n?(await nt()).find(s=>s.workspace?.branch===n&&pm(s,t,r))??null:null}async function yv({repo:t=null,number:e=null,branch:n=null}){let r=await cl({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&&pm(a,t,o))??null}async function xe(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await cl({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 s1(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 Ht(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&pm(a,n.path,r));s&&await ee({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 ek();await s1(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:d}=t.body||{};try{if(s){let w=o||"merge";if(!Ws.has(w))throw new Error(`invalid merge method: ${w}`);let b=Number(r);if(!Number.isInteger(b)||b<=0)throw new Error("invalid PR number");let k=await rt((await Qe(n)).path);if(!k)throw new Error("repo has no GitHub remote");let S=await cl({repo:n,number:b,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 qs(n,b),await ee({repo:k,number:b,trackId:S.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Os(k,b,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await gr(k,b,i===!0),await Cs(k,b,c===!0&&i!==!0),Me(),await xe("auto-armed",{number:b,url:d,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:b,auto:!0})}let f=await So(n,r,{method:o||"squash"});await xe("merged",{number:f.number,url:d,title:u,branch:l,repo:n});let p=await rt(n).catch(()=>null),m=!1;if(p&&(i===!0&&await gr(p,f.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Cs(p,f.number,!0).catch(()=>{}),await Je(p,f.number).catch(()=>{}),m=await ze(p,f.number).catch(()=>!1),await Pn(p,f.number,"merged").catch(()=>{})),i===!0&&!m){let w=await cl({repo:n,number:f.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await _r(w.id).catch(()=>{})}e.json(f)}catch(f){let p=f?.message||String(f),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 Qe(n)).path).catch(()=>null),l=c?await Ms(c,i).catch(()=>null):null;try{await nf(n,i)}catch(u){if(!l)throw u}Me(),await xe("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 sk(n,{method:r||"merge",onMerged:a=>{xe("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Je(o,a.number).catch(()=>{}).then(()=>ze(o,a.number)).catch(()=>{}).then(()=>Pn(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:ok()})});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(!Ws.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 yr()).filter(f=>f.trackId===c.id&&f.state!=="merged"&&f.state!=="closed");if(!l.length)return e.status(409).json({error:"no open PRs on this Track"});for(let f of l)await gr(f.repo,f.number,o===!0).catch(()=>{}),await Cs(f.repo,f.number,s===!0&&o!==!0).catch(()=>{});let u=[],d=[];for(let f of l){let p={repo:f.repo,number:f.number,ok:!0,action:"armed"};u.push(p);try{let m=await xr(f.repo);if(!m)throw new Error(`no local checkout for ${f.repo}`);let w=await xo(m,f.number);d.push({r:p,l:f,repoPath:m,pr:w,plan:nk(w,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:f,l:p,repoPath:m,pr:w,plan:b}of rk(d))try{if(b.action==="skip")f.action="skipped",f.reason=b.reason;else if(b.action==="merge")await So(m,p.number,{method:i}),await xe("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await Je(p.repo,p.number).catch(()=>{}),await ze(p.repo,p.number).catch(()=>{}),await Pn(p.repo,p.number,"merged").catch(()=>{}),f.action="merged";else{if(b.action==="update-arm"&&(await qs(m,p.number).catch(()=>{}),f.action="updated-armed"),!await Os(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await xe("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c,...f.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){f.ok=!1,f.action="failed",f.reason=k?.message||String(k)}if(Me(),!u.some(f=>f.ok)){if(o===!0)for(let f of l)await gr(f.repo,f.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let f of l)await Cs(f.repo,f.number,!1).catch(()=>{});return e.status(502).json({error:u.map(f=>`${f.repo}#${f.number}: ${f.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 yr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Ms(a.repo,a.number).catch(()=>null),c=await xr(a.repo).catch(()=>null);c&&await nf(c,a.number).catch(()=>{}),i&&(await xe("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}))}Me(),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=fe(i.workspace),d,f=Array.isArray(a)?a.filter(Boolean):[];if(f.length)d=f.map(w=>{let b=u.find(k=>k.name===w||k.repo===w||k.base===w||k.dir===w);if(!b)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return b});else if(d=await go(i),!d.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of d)try{let b=await of(w.dir,{title:r,body:o,base:s}),k=w.repo||await rt(w.dir).catch(()=>null);k&&b.number&&await ee({repo:k,number:b.number,trackId:i.id,url:b.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await xe("opened",{number:b.number,url:b.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??w.name}}),p.push({repo:w.name,slug:k??null,ok:!0,url:b.url,number:b.number})}catch(b){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:b?.message||String(b)})}let m=p.filter(w=>w.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(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await of(i.cwd,{title:r,body:o,base:s}),l=await rt(i.cwd).catch(()=>null);l&&c.number&&await ee({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await xe("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 go(n),o=new Map(r.map(a=>[a.base,a])),s=fe(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 Vf(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 zf(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 xo(n,r),s=await rt(n).catch(()=>null),a=await yv({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=fe(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await ak(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?dt.join("ci",i.name):"ci",d=dt.join(a.cwd,".bostrat",u),f=dt.join(".bostrat",u,`pr-${o.number}-fail.log`);await Lt.promises.mkdir(d,{recursive:!0}),await Lt.promises.writeFile(dt.join(a.cwd,f),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
427
452
  ${k.log??"(no fetchable log \u2014 see link)"}
428
453
  `).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 Ix({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:C0(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 Kl({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,Zl],["jira",Ja,Xl]],xf=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await Jl(),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 Jl()})}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 uu(t.params.id)})});var gc=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(gc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await du(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(gc(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(gc(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(gc(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=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 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=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 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 Vl(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 Vl(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=D0(t.query.since||null);e.json({since:n,repos:df(),prs:await B0(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 j0(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,wc=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=ts(`${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:wc?wc.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 hx(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 ns(),terms:await bf(),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 px(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 wx())});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 gx())}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 yx(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 Ld({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:Rx(Cs(t.params.id)),subtasks:mo(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 uu(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 po(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 Pd(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}
454
+ `));let p=l.map(k=>k.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await de(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 ${f} 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"}),b=s;b&&(await ee({repo:b,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Jy(b,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await xe("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:w.leadBox,queued:!!w.queued,checks:l.map(k=>k.name),log:f})}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 xo(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 yv({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=fe(a.workspace).find(d=>d.base===n||s&&d.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await de(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 ee({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Yy(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await xe("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=Di.filter(d=>t.body.actions.includes(d)),!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 F(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return e.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await Qp(a,c,{actions:o,notify:s});if(d)return e.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await de(a.id,mo(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&&Wf(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})}});x.post("/api/tracks/:id/pr-exists",async(t,e)=>{let{offerId:n,choice:r}=t.body||{};try{let o=await z(t.params.id);if(!o)return e.status(404).json({error:"track not found"});let{say:s}=await tm(o,{offerId:n,choice:r});e.json({ok:!0,offerId:n,choice:r,queued:!!s?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/unknown offer/i.test(s)?404:/already answered|not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});var Ca=t=>e=>({...e,url:bS(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await Vd({trackId:t.query.track||void 0});e.json({views:n.map(Ca(t)),maxLive:jn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await Lb(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Ca(t)(n),session:yo(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+dt.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Jd({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:Ca(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:Ca(t)(await Fb(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:Ca(t)(await Ub(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 Hb(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var a1=[["linear",Rr,mp],["jira",Cc,fp]],cm=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await dp(),r={};for(let[o,s,a]of a1){let i=await s();if(!i){cm.delete(o),r[o]={configured:!1};continue}let c=cm.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},cm.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 dp()})}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 Vx(o),i=n?await z(n):null;!i&&r&&(i=(await nt()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+dt.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})}});var bv=async(t,e)=>{let n=t?await z(t):null;return!n&&e&&(n=(await nt()).find(o=>o.cwd&&(e===o.cwd||e.startsWith(o.cwd+dt.sep)))||null),n};x.get("/api/circuits",async(t,e)=>{try{e.json({circuits:await cp()})}catch(n){e.status(502).json({error:n?.message||String(n)})}});x.post("/api/circuits/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=await bv(n,r);if(!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 (circuit name or id) required"});let a=await lp(o);await Rc(s,a,{source:"chat"}),e.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);e.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});x.post("/api/circuits/unlink",async(t,e)=>{let{trackId:n,cwd:r}=t.body||{};try{let o=await bv(n,r);if(!o)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Rc(o,null,{source:"chat"}),e.json({ok:!0,trackId:o.id})}catch(o){e.status(502).json({error:o?.message||String(o)})}});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+dt.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 jo(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+dt.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 l0(),ticket:await Le(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 Le(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 jo(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 Ep(t.params.id)})});var ul=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 p0(t.params.id))}catch(n){let r=n?.message||String(n);e.status(ul(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await _p(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(ul(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await m0(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(ul(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 h0(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(ul(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 tp(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+dt.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 g0(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 w0()})}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 y0(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 zs({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 tn(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 tn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=be(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Gs(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 tn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=be(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Gs(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 tn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=be(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(ce(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&&cc(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 d=a?await z(a):null;if(!d&&c&&(d=(await nt()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+dt.sep)))||null),!d)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=(d.boxes||[]).find(m=>m.name===d.leadBox),p=await bk({trackId:d.id,name:i,html:n?null:s.html,image:r,video:o,cwd:d.cwd,width:l,group:u,box:d.leadBox,agent:f?.agent||null});e.json({ok:!0,mock:p})}catch(d){let f=d?.message||String(d);e.status(/required|invalid|must be/i.test(f)?400:500).json({error:f})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&cc(n)?n:null;try{let o=await kk(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 vk(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await de(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 Uk(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Tk(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 Dk({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 Bk(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+dt.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 jk({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 Lk(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 Fk(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+dt.sep)))?.id,!n)return e.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await Jk({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 kf(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 kf(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Vk(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 Yk(n,r.v);return typeof o!="string"?e.status(404).json({error:"runbook artifact missing"}):e.send(o)}e.sendFile(on(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 kS())});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+dt.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 Xk({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 Zk(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 Qk(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 tx(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=vS(t.query.since||null);e.json({since:n,repos:Vp(),prs:await TS(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+dt.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await AS(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 i1=120,lm=400,ll=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>lm)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${lm} cap; a notification is a nudge, put the detail in chat`});let c=Js(`${n||""}
455
+ ${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+dt.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),d=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),f={title:n?d(n,i1):l.name,body:d(i,lm),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:f}),e.json({ok:!0,notification:f,delivered:ll?ll.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 sv(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 pa(),terms:await am(),problem:await ov()})});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 rv(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 av())});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 iv())}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 cv(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,circuit:l}=t.body||{};try{let u=await Sc({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),d=null;if(c)try{await jo(u,c,{source:"create"})}catch(p){d=p?.message||String(p)}let f=null;if(l)try{await Rc(u,await lp(l),{source:"picker"})}catch(p){f=p?.message||String(p)}e.json({ok:!0,track:u,...d?{ticketWarning:d}:{},...f?{circuitWarning:f}:{}})}catch(u){let d=u?.message||String(u);e.status(/required|unknown agent|invalid|must be/i.test(d)?400:500).json({error:d})}});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:kv(No(t.params.id)),subtasks:Ir(t.params.id).map(Ma)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Xf(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await Ep(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 _r(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 Zf(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 yc(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
456
  data: ${JSON.stringify(i)}
432
457
 
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
458
+ `),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await F(n))i.seq>o&&r(i);let s=zh(n.id,r),a=setInterval(()=>e.write(`: hb
434
459
 
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=Uo(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||!Sd(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(vd(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?Xc(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 jd(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 Dd(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 rc(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 $k(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:mo(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(Mk(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 Hd(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(Rx(Cs(t.params.id))??{running:!1})});function Rx(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(Tx,"..","..","client","dist");Gt.existsSync(Sf)&&(x.use(fe.static(Sf)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ft.join(Sf,"index.html"))));var $x=XP.createServer(x),oD=new ZP({noServer:!0}),Ox=(t,e,n)=>{if(!(Us(t)||So.allowed(So.clientIp(t))||Af.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
460
+ `),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=ws(await F(n)),a=fi(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 Um()})});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 Hm(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 c1=["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(`
461
+ `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await de(t.params.id,c1)})}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 Ac(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 de(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 ax(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=dt.basename(String(t.params.file||""));if(!r||!vf(dt.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");let o=Sf(n.cwd),s,a;try{s=await Lt.promises.realpath(o),a=await Lt.promises.realpath(dt.join(o,r))}catch{return e.status(404).json({error:"upload not found"})}if(!a.startsWith(s+dt.sep))return e.status(404).json({error:"upload not found"});e.sendFile(r,{root:o},i=>{i&&!e.headersSent&&e.status(404).json({error:"upload not found"})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});var l1=/^[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(!l1.test(o)||!Number.isInteger(s)||s<0)return e.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(f=>f.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return e.status(404).json({error:"image source not available"});let i=We(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=t1.createInterface({input:Lt.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let f of u){if(!f.includes(c))continue;let p;try{p=JSON.parse(f)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let d=l?Kl(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return e.status(404).json({error:"image not found"});e.set("content-type",d.mediaType),e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.send(Buffer.from(d.data,"base64"))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await kc(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 Hf(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 bc(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 Yf({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 Ff(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 Kf(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 Jc(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await Gf(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 sp(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Ma(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:Ir(t.params.id).map(Ma)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await Lf(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:Ma(Ic(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:Ma(ap(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Ma(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 Ec(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 qf(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(kv(No(t.params.id))??{running:!1})});function kv(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:Ja(),paths:sh,trace:await oh({limit:n})})});var um=dt.resolve(mv,"..","..","client","dist");Lt.existsSync(um)&&(x.use(Se.static(um)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(dt.join(um,"index.html"))));var xv=n1.createServer(x),u1=new o1({noServer:!0}),Sv=(t,e,n)=>{if(!(Ta(t)||Vo.allowed(Vo.clientIp(t))||fm.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
437
462
  \r
438
- `),e.destroy();return}if(M0(t,e,n))return;if(!R0(t,_x,{enforce:Ex})){e.write(`HTTP/1.1 401 Unauthorized\r
463
+ `),e.destroy();return}if(yS(t,e,n))return;if(!wS(t,wv,{enforce:gv})){e.write(`HTTP/1.1 401 Unauthorized\r
439
464
  \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
465
+ `),e.destroy();return}let r=yh(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
441
466
  \r
442
- `),e.destroy();return}let o=r.sessionName;if(!wt(o)){e.write(`HTTP/1.1 400 Bad Request\r
467
+ `),e.destroy();return}let o=r.sessionName;if(!kt(o)){e.write(`HTTP/1.1 400 Bad Request\r
443
468
  \r
444
- `),e.destroy();return}let{cols:s,rows:a}=r;oD.handleUpgrade(t,e,n,i=>Ip(i,o,{cols:s,rows:a}))};$x.on("upgrade",Ox);var Mx=process.env.BOSTRAT_CERT_DIR||ft.join(process.env.HOME||Tf.tmpdir(),".bostrat","certs"),xx=Number(process.env.BOSTRAT_TLS_PORT)||3443,vx=ft.join(Mx,"bostrat.globopi.key"),Sx=ft.join(Mx,"bostrat.globopi.crt");if(Gt.existsSync(vx)&&Gt.existsSync(Sx)){let t=YP.createServer({key:Gt.readFileSync(vx),cert:Gt.readFileSync(Sx)},x);t.on("upgrade",Ox),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(xx,mc,()=>{console.log(`[bostrat-agent] https listening on ${mc||"0.0.0.0"}:${xx}`)})}$x.listen(bx,mc,()=>{console.log(`[bostrat-agent] listening on ${mc||"0.0.0.0"}:${bx}`),console.log(`[bostrat-agent] tmux socket: ${zt}`),console.log(`[bostrat-agent] auto-trigger default: ${Ax}%`),console.log(`[bostrat-agent] ip allowlist: ${So.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)),Rk().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)),f0()&&p0({listTracks:st}),q0(),Lw({resolveTrack:Q}),Hk({listTracks:st,createTrack:Ld,archiveTrack:po,listRepos:Oe,resolveTrack:Q,sayToLead:ue,answerPrompt:Dd,startLeadHandoff:Hd,interruptLead:jd,listSubtaskJobs:mo,cancelSubtask:qd,readEvents:B,appendEvent:$,knownAgents:Bi}),sD(),Po().then(t=>{t||console.warn(`[boot] no host tmux server on ${zt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),ox(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(wc=Vk({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:Nd,sampleMetrics:Jk}),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{wc?.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(Tx,"..","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 l0({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 Pd(n.id).catch(()=>{}),await hb(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
469
+ `),e.destroy();return}let{cols:s,rows:a}=r;u1.handleUpgrade(t,e,n,i=>hh(i,o,{cols:s,rows:a}))};xv.on("upgrade",Sv);var vv=process.env.BOSTRAT_CERT_DIR||dt.join(process.env.HOME||dm.tmpdir(),".bostrat","certs"),dv=Number(process.env.BOSTRAT_TLS_PORT)||3443,fv=dt.join(vv,"bostrat.globopi.key"),pv=dt.join(vv,"bostrat.globopi.crt");if(Lt.existsSync(fv)&&Lt.existsSync(pv)){let t=r1.createServer({key:Lt.readFileSync(fv),cert:Lt.readFileSync(pv)},x);t.on("upgrade",Sv),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(dv,il,()=>{console.log(`[bostrat-agent] https listening on ${il||"0.0.0.0"}:${dv}`)})}xv.listen(lv,il,()=>{console.log(`[bostrat-agent] listening on ${il||"0.0.0.0"}:${lv}`);let t=O0(process.env);t&&console.warn(`[bostrat-agent] \u26A0 SECURITY: plane-linked node bound to ${t.host?t.host:"ALL interfaces (BOSTRAT_HOST=all)"} \u2014 the API + Views are reachable off-loopback (LAN/VPN), where the IP allowlist is spoofable and Views need no token. The plane tunnel does not require this; set BOSTRAT_HOST=127.0.0.1 once any local reverse-proxy/WireGuard path is retired (F-2).`),console.log(`[bostrat-agent] tmux socket: ${Qt}`),console.log(`[bostrat-agent] auto-trigger default: ${hv}%`),console.log(`[bostrat-agent] ip allowlist: ${Vo.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(fm.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),np().then(e=>e&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(e=>console.error("[bostrat-agent] handoff reconcile failed:",e?.message||e)),rp().then(e=>e&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(e=>console.error("[bostrat-agent] subtask reconcile failed:",e?.message||e)),Qf().then(e=>e&&console.log(`[bostrat-agent] reaped ${e} orphaned box(es) on archived track(s)`)).catch(e=>console.error("[bostrat-agent] archived-orphan sweep failed:",e?.message||e)),Jf().then(e=>console.log(`[bostrat-agent] standing instructions refreshed (${e.refreshed} updated, ${e.removed} legacy CLAUDE.md removed${e.codexGlobal?`, codex global ${e.codexGlobal}`:""})`)).catch(e=>console.error("[bostrat-agent] standing-instructions refresh failed:",e?.message||e)),m1(),p1(),ly({listTracks:nt,getTrack:z}).then(e=>console.log(`[bostrat-agent] spend caps ${e.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(e.spent).length} provider counter(s)`)).catch(e=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",e?.message||e)),Q0()&&tS({listTracks:nt}),OS(),PS(),b0({resolveTrack:z}),R0({listTracks:nt,createTrack:Sc,archiveTrack:_r,listRepos:Fe,resolveTrack:z,sayToLead:de,answerPrompt:bc,startLeadHandoff:Ec,interruptLead:kc,listSubtaskJobs:Ir,cancelSubtask:Ic,readEvents:F,appendEvent:I,divertShip:Qp,answerPrExists:tm,knownAgents:ia}),d1(),ls().then(e=>{e||console.warn(`[boot] no host tmux server on ${Qt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),zS(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(ll=P0({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||dm.hostname(),name:process.env.BOSTRAT_NODE_NAME||dm.hostname(),listTracks:nt,detectAgents:wc,sampleMetrics:D0}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),Lr().catch(()=>{}),rx().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} runbooks to the plane`),()=>{}),k0().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} ticket links to the plane`),()=>{}),_k().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} mocks to the plane`),()=>{}),qk().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} flows to the plane`),()=>{}),nb().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{ll?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function d1(){f1(),tc().catch(()=>{}),setInterval(()=>tc().catch(()=>{}),1e4)}function f1(){for(let t of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-runbook","bostrat-loop","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let e=dt.resolve(mv,"..","bin",t),n=dt.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Lt.existsSync(e))continue;Lt.mkdirSync(n,{recursive:!0});let r=dt.join(n,t);(Lt.existsSync(r)?Lt.readlinkSync(r):null)!==e&&(Lt.rmSync(r,{force:!0}),Lt.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 p1(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await nt(),r=await Y0({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 m1(){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 yc(n.id).catch(()=>{}),await ik(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}