bostrat-node 0.2.23 → 0.2.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +128 -128
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,85 +1,85 @@
1
- var Nx=Object.defineProperty;var Ef=(t,e)=>{for(var n in e)Nx(t,n,{get:e[n],enumerable:!0})};import ft from"node:path";import Gt from"node:fs";import zP from"node:readline";import{fileURLToPath as JP}from"node:url";import fe from"express";import XP from"node:http";import YP from"node:https";import Tf from"node:os";import{WebSocketServer as ZP}from"ws";import{execFile as nS}from"node:child_process";import{promisify as rS}from"node:util";import ma from"node:fs";import{randomUUID as oS}from"node:crypto";import Vt from"node:fs/promises";import Of from"node:os";import Lt from"node:path";import{execFile as Lx}from"node:child_process";import{promisify as Fx}from"node:util";import{createHash as Px,randomBytes as Dx,timingSafeEqual as Bx}from"node:crypto";var Zs="BOSTRAT_HOOK_TOKEN",Qs="x-bostrat-hook";function If(){return Dx(32).toString("hex")}function Ao(t){let e=typeof t=="string"?t.trim():"";return e?Px("sha256").update(e).digest("hex"):null}function Rf(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:Bx(Buffer.from(t),Buffer.from(e))}var gc=new Map,yc=new Map,jx=6e4;function ta(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(gc.set(t,(gc.get(t)||0)+1),yc.has(t)&&n-yc.get(t)<jx)return!1;yc.set(t,n);let o=Object.entries(e).filter(([,s])=>s!=null&&s!=="").map(([s,a])=>`${s}=${a}`).join(" ");return r(`[hook-audit] ${t} n=${gc.get(t)}${o?` ${o}`:""}`),!0}async function $f(t,{byToken:e,legacy:n}){let r=typeof t=="string"?t.trim():"";if(!r)return{mode:"legacy",target:await n()};let o=await e(r);return o?.track?{mode:"token",target:o}:{mode:"refused",target:null}}function ea(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Ux(){return process.env.CLAUDE_CONFIG_DIR?Lt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Lt.join(Of.homedir(),".claude.json")}function Hx(){return process.env.CLAUDE_CONFIG_DIR?Lt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Lt.join(Of.homedir(),".claude","settings.json")}async function on(t){if(!t||typeof t!="string")return;let e=Lt.resolve(t),n=Ux(),r=Hx(),o={};try{o=JSON.parse(await Vt.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[e]=o.projects[e]||{};let s=o.projects[e];Array.isArray(s.allowedTools)||(s.allowedTools=[]),s.hasTrustDialogAccepted=!0,s.hasCompletedProjectOnboarding=!0;let a=process.env.ANTHROPIC_API_KEY;if(a){let c=a.slice(-20);o.customApiKeyResponses=o.customApiKeyResponses||{},o.customApiKeyResponses.approved=o.customApiKeyResponses.approved||[],o.customApiKeyResponses.rejected=o.customApiKeyResponses.rejected||[],o.customApiKeyResponses.approved.includes(c)||o.customApiKeyResponses.approved.push(c)}await Vt.mkdir(Lt.dirname(n),{recursive:!0}),await Vt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Vt.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,process.env.BOSTRAT_HOOKS!=="off"){let l=`curl -sS -m 3 -X POST ${process.env.BOSTRAT_HOOK_URL||`http://127.0.0.1:${process.env.PORT||3362}/api/hook`} -H 'content-type: application/json' -H "${Qs}: $${Zs}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let f of["SessionStart","UserPromptSubmit","Stop"])i.hooks[f]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await Vt.mkdir(Lt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(i,null,2)),await Wx(e)}async function Wx(t){try{let e=Lt.resolve(t),n=Lt.join(e,".bostrat","memory","claude"),r=Lt.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await Vt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Lt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Vt.mkdir(n,{recursive:!0}),await Vt.mkdir(Lt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(o,null,2)),await qx(e,["/.claude/settings.local.json"])}catch{}}async function qx(t,e){try{let{stdout:n}=await Fx(Lx)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Lt.resolve(t,n.trim()),o=await Vt.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
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
2
  `).includes(a));if(!s.length)return;await Vt.mkdir(Lt.dirname(r),{recursive:!0}),await Vt.appendFile(r,(o&&!o.endsWith(`
3
3
  `)?`
4
4
  `:"")+s.join(`
5
5
  `)+`
6
- `)}catch{}}import bc from"node:fs/promises";import Kx from"node:os";import na from"node:path";function Gx(){let t=process.env.CODEX_HOME||na.join(Kx.homedir(),".codex");return na.join(t,"config.toml")}function Mf(t){return`[projects."${t}"]`}function Vx(t,e){let n=Mf(e),r=t.indexOf(n);if(r===-1)return!1;let o=t.slice(r+n.length),s=o.search(/^\s*\[/m),a=s===-1?o:o.slice(0,s);return/trust_level\s*=\s*"trusted"/.test(a)}async function yr(t){if(!t||typeof t!="string")return;let e=na.resolve(t),n=Gx(),r="";try{r=await bc.readFile(n,"utf8")}catch{}if(Vx(r,e))return;let o=Mf(e);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),f=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
6
+ `)}catch{}}import 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*$/,"")}
7
7
  trust_level = "trusted"
8
8
  `;r=r.slice(0,a)+f+r.slice(l)}else{let s=`
9
9
  ${o}
10
10
  trust_level = "trusted"
11
11
  `;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
12
12
  `)?`
13
- `:"")}try{await bc.mkdir(na.dirname(n),{recursive:!0}),await bc.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Cf from"node:fs/promises";import zx from"node:os";import _o from"node:path";function Jx(t){let n=_o.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||_o.join(zx.homedir(),".cursor");return _o.join(r,"projects",n)}async function br(t){if(!t||typeof t!="string")return;let e=_o.resolve(t),n=Jx(e),r=_o.join(n,".workspace-trusted");await Cf.mkdir(n,{recursive:!0}),await Cf.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
- `,"utf8")}import L from"node:fs/promises";import vv from"node:os";import X from"node:path";import{execFile as Sv}from"node:child_process";import{promisify as Tv}from"node:util";import av from"node:fs";import Pf from"node:fs/promises";import Io from"node:path";import iv from"node:os";import{fileURLToPath as cv}from"node:url";import{execFile as lv}from"node:child_process";import{promisify as uv}from"node:util";import xc from"node:fs/promises";import sn from"node:path";import{execFile as Xx}from"node:child_process";import{promisify as Yx}from"node:util";var Zx=Yx(Xx),Nf=process.env.HOME||"/home/globo",Qx=sn.join(process.env.BOSTRAT_STATE_DIR||sn.join(Nf,".bostrat"),"repos"),tv=[...(process.env.BOSTRAT_REPOS_ROOT||sn.join(Nf,"Documents")).split(","),Qx].map(t=>t.trim()).filter(Boolean),ev=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,nv=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function rv(t){try{return(await xc.stat(sn.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function ov(t){let e=i=>xc.readFile(sn.join(t,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await e("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await e(`refs/heads/${i}`)||(o??=await e("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await e("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function Oe(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await rv(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:sn.basename(o),branch:await ov(o)});return}if(s>=ev)return;let i;try{i=await xc.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||nv.has(c.name)||await e(sn.join(o,c.name),s+1)}for(let o of tv)await e(sn.resolve(o),0);let n=new Set,r=[];for(let o of t.values()){let s=(await et(o.path))?.toLowerCase();if(s){if(n.has(s))continue;n.add(s)}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function sv(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var kc=new Map;async function et(t){if(!t)return null;if(kc.has(t))return kc.get(t);let e=null;try{let{stdout:n}=await Zx("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=sv(n)}catch{e=null}return kc.set(t,e),e}async function Eo(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await Oe())if((await et(n.path))?.toLowerCase()===e)return n.path;return null}var Df=uv(lv),dv=process.env.BOSTRAT_STATE_DIR||Io.join(process.env.HOME||iv.tmpdir(),".bostrat"),fv=Io.join(dv,"repos"),Bf=/^[^/\s]+\/[^/\s]+$/,pv=5*6e4,mv=6e4,hv=10*6e4,jf=fetch,vc=new Map,Fn=null;function Qt(t=process.env){let e=t.BOSTRAT_PLANE_URL||"";if(!e)return null;try{let n=new URL(e);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function ra(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Lf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),kr=(t,e)=>Object.assign(new Error(t),{status:e});async function oa(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Bf.test(n))throw kr("repo must be owner/name",400);let r=vc.get(n);if(r&&r.expiresAt-Date.now()>pv)return r;if(!ra(e))throw kr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await jf(`${Qt(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Lf(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw kr(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if(o.status>=500&&r&&r.expiresAt>Date.now())return r;throw kr(s?.error||`plane replied ${o.status}`,o.status)}let a={token:s.token,expiresAt:Number(s.expiresAt)||Date.now()+55*6e4,username:s.username??null,installationId:s.installationId??null};return vc.set(n,a),a}async function Ro({env:t=process.env,force:e=!1}={}){if(!ra(t))return{configured:!1,repos:[],username:null};if(!e&&Fn&&Date.now()-Fn.at<mv)return Fn.data;try{let n=await jf(`${Qt(t)}/v1/github/repos`,{headers:Lf(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Fn={at:Date.now(),data:r},r}catch(n){return Fn?Fn.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Ff(){vc.clear(),Fn=null}async function me({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await et(t):null);if(!r)return{};let{token:o}=await oa(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Sc({env:t=process.env}={}){return(await Ro({env:t})).username??null}var wv=()=>cv(new URL("../bin/bostrat-git-credential",import.meta.url)),gv=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function yv(t){let[e,n]=String(t).split("/");return Io.join(fv,e,n)}async function Tc(t,{env:e=process.env}={}){let n=(...r)=>Df("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${wv()}" ${gv(e)}`),await n("config","credential.useHttpPath","true")}async function Ac(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Bf.test(String(t||"")))throw kr(`bad repo slug "${t}"`,400);let r=yv(t);if(av.existsSync(Io.join(r,".git")))return r;let{token:o}=await oa(t,{env:e});await Pf.mkdir(Io.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Df("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:hv,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Pf.rm(r,{recursive:!0,force:!0}).catch(()=>{}),kr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Tc(r,{env:e}),r}var bv=6e4,kv=5e3,_c=new Map,xv=(...t)=>fetch(...t);function Uf(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Hf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Wf(t,e,n){let r=Qt(n),o=new AbortController,s=setTimeout(()=>o.abort(),kv);try{return await xv(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function qf(t,{env:e=process.env,force:n=!1}={}){if(!Uf(e))return{facts:[],source:"unconfigured"};let r=_c.get(t);if(!n&&r&&Date.now()-r.at<bv)return{facts:r.facts,source:"cache"};try{let o=await Wf(`/v1/memory/${encodeURIComponent(t)}`,{headers:Hf(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return _c.set(t,{at:Date.now(),facts:a}),{facts:a,source:"plane"}}catch(o){return r?{facts:r.facts,source:"stale",error:o?.message}:{facts:[],source:"unreachable",error:o?.message}}}async function sa(t,e,{env:n=process.env}={}){if(!Uf(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await Wf(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Hf(n),"content-type":"application/json"},body:JSON.stringify({facts:e})},n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.results)?o.results:[];return _c.delete(t),{pushed:s.filter(a=>a.outcome==="accepted").length,rejected:s.filter(a=>a.outcome==="rejected"),source:"plane"}}catch(r){return{pushed:0,source:"unreachable",error:r?.message}}}var Av=Tv(Sv),Ec=process.env.BOSTRAT_STATE_DIR||X.join(process.env.HOME||vv.tmpdir(),".bostrat"),Kf=200,_v=25*1024;function aa(){return X.join(Ec,"memory","repos")}var Me="operator";function Un(t){return t===Me?X.join(Ec,"memory",Me):X.join(aa(),t)}var Jf=".tombstones.json";async function $o(t){try{return JSON.parse(await L.readFile(X.join(t,Jf),"utf8"))}catch{return{}}}async function Ic(t,e){let n=await $o(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await L.mkdir(t,{recursive:!0}),await L.writeFile(X.join(t,Jf),JSON.stringify(n))}function Gf(t){let e=String(t||"").trim();if(!e)return null;let r=(e.replace(/\/+$/,"").split(/[/:]/).pop()||"").replace(/\.git$/i,"").toLowerCase();return/^[a-z0-9][a-z0-9._-]*$/.test(r)?r:null}async function Vf(t){try{let{stdout:e}=await Av("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function Rc(t){if(!t)return[];let e=Gf(await Vf(t));if(e)return[e];let n=[],r=[];try{r=await L.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Gf(await Vf(X.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function ia(t){let e=/^---\n([\s\S]*?)\n---\n?/.exec(t);return e?{fm:e[1],body:t.slice(e[0].length)}:{fm:null,body:t}}function an(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function he(t,e=null){let n=e??await L.readFile(t,"utf8").catch(()=>""),{fm:r}=ia(n),o=an(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await L.stat(t)).mtimeMs}catch{return 0}}function Xf(t,e){let n=an(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Me){let o=an(t,"trust");if(!o||/^operator/i.test(o))return Me}return e}var xr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function $c(t,e,n,r=new Map){let o=(t||"").split(`
13
+ `:"")}try{await 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(`
15
15
  `),s=new Map;for(let c of(e||"").split(`
16
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(`
17
17
  `).replace(/\n{3,}/g,`
18
18
 
19
- `)}function Yf(t,e){let n=an(e,"name")||t.replace(/\.md$/,""),r=an(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function Oo(t){let e=[];try{e=await L.readdir(t,{withFileTypes:!0})}catch{return[]}return e.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function Zf(t,e){let n=X.join(t,".bostrat","memory"),r=await Oo(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=new Map,s=new Map,a=0,i=new Map;for(let l of r){let u=X.join(n,l),f;try{f=await L.readFile(u,"utf8")}catch{continue}let{fm:d}=ia(f),p=Xf(d,e);if(!p)continue;let m=Un(p),h=X.join(m,l),y=await he(u,f);if(i.has(m)||i.set(m,await $o(m)),(i.get(m)[l]||0)>=y)continue;let k=await he(h).catch(()=>0);if(await L.access(h).then(()=>!0).catch(()=>!1)&&k>=y)continue;await L.mkdir(m,{recursive:!0}),await L.writeFile(h,f),a++;let I=o.get(m);I||o.set(m,I={won:new Set,generated:new Map}),I.won.add(l),I.generated.set(l,Yf(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await L.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=X.join(l,"MEMORY.md"),d=await L.readFile(f,"utf8").catch(()=>"");await L.writeFile(f,$c(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Ev(){let t=X.join(Ec,"workspaces"),e=[];try{e=await L.readdir(t,{withFileTypes:!0})}catch{return{harvested:0}}let n=0,r=new Map;for(let o of e){if(!o.isDirectory())continue;let s=X.join(t,o.name);if(!await L.access(X.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await Rc(s),c=await Zf(s,i||null);n+=c.harvested;for(let[l,u]of c.changedByKey){r.has(l)||r.set(l,new Set);for(let f of u)r.get(l).add(f)}}return{harvested:n,changedByKey:r}}var Iv=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,Rv=t=>{let e=new Map;for(let n of(t||"").split(`
20
- `)){let r=xr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function $v(t,e){let n=Un(t),r=await $o(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!Iv.test(l)||l==="MEMORY.md")continue;let u=X.join(n,l),f=await L.access(u).then(()=>!0).catch(()=>!1),d=f?await he(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await L.rm(u,{force:!0}),await Ic(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await L.mkdir(n,{recursive:!0}),await L.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=X.join(n,"MEMORY.md"),l=await L.readFile(c,"utf8").catch(()=>""),u=$c(l,[...s.values()].join(`
19
+ `)}function 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
21
  `),o);if(a.length){let f=new Set(a);u=u.split(`
22
22
  `).filter(d=>!f.has(xr(d))).join(`
23
- `)}await L.mkdir(n,{recursive:!0}),await L.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Oc(t,e=null){let n=Un(t),r=e?.length?e:await Oo(n);if(!r.length)return{pushed:0,source:"empty"};let o=Rv(await L.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=X.join(n,a),c;try{c=await L.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await he(i,c),indexLine:o.get(a)??null})}return sa(t,s)}async function Ov(t){let{facts:e,source:n}=await qf(t);e.length?await $v(t,e):n==="plane"&&await Oc(t)}function Mv(t){let e=t.split(`
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
24
  `);e.length>Kf&&(e=e.slice(0,Kf));let n=e.join(`
25
- `);for(;Buffer.byteLength(n,"utf8")>_v&&e.length;)e.pop(),n=e.join(`
26
- `);return n}var Cv=`# Track memory
25
+ `);for(;Buffer.byteLength(n,"utf8")>Ev&&e.length;)e.pop(),n=e.join(`
26
+ `);return n}var Nv=`# Track memory
27
27
 
28
28
  Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
29
29
  index and the fact files beside it. Facts you save here stay Claude-local but ride the
30
30
  same sync; prefer adding durable cross-provider facts to \`../\` following its format.
31
- `;async function Nv(t,e){if(!t||!e?.length)return{materialized:!1,reason:"no-repo-key"};let n=X.join(t,".bostrat","memory"),r=0,o=0,s=[];for(let u of e){let f=Un(u),d=await $o(f);for(let[y,k]of Object.entries(d)){let v=X.join(n,y);await L.access(v).then(()=>!0).catch(()=>!1)&&await he(v)<=k&&(await L.rm(v,{force:!0}),o++)}let p=await Oo(f);if(!p.length)continue;await L.mkdir(n,{recursive:!0});for(let y of p){let k=X.join(f,y),v=X.join(n,y),I=await he(k),R=await he(v).catch(()=>0);await L.access(v).then(()=>!0).catch(()=>!1)&&R>=I||(await L.copyFile(k,v),r++)}let m=await L.readFile(X.join(f,"MEMORY.md"),"utf8").catch(()=>""),h=u===Me?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${h}
31
+ `;async function 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}
32
32
 
33
33
  ${m.trim()}`:m.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
34
34
 
35
35
  `)+`
36
- `,c=Mv(i);await L.mkdir(n,{recursive:!0}),await L.writeFile(X.join(n,"MEMORY.md"),c);let l=X.join(n,"claude");return await L.mkdir(l,{recursive:!0}),await L.writeFile(X.join(l,"MEMORY.md"),Cv,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var zf=0,Pv=3e4;async function Qf(t){try{let e=await Rc(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Me];if(Date.now()-zf>Pv){zf=Date.now(),await Dv();let{changedByKey:r}=await Ev();for(let[o,s]of r)await Oc(o,[...s])}for(let r of n)await Ov(r);return await Nv(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function Dv(){let t=0,e=[];try{e=await L.readdir(aa(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=X.join(aa(),n.name),o=Un(Me),s=new Set,a=new Map;for(let p of await Oo(r)){let m=X.join(r,p),h;try{h=await L.readFile(m,"utf8")}catch{continue}let{fm:y}=ia(h);if(Xf(y,n.name)!==Me)continue;let k=X.join(o,p),v=await he(k).catch(()=>0),I=await he(m,h);await L.mkdir(o,{recursive:!0}),I>v&&await L.writeFile(k,h),await L.rm(m,{force:!0}),s.add(p),a.set(p,Yf(p,y)),t++}if(!s.size)continue;let i=Date.now();await Ic(r,[...s].map(p=>[p,i])),await sa(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=X.join(r,"MEMORY.md"),l=await L.readFile(c,"utf8").catch(()=>""),u=l.split(`
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
37
  `).filter(p=>!s.has(xr(p))).join(`
38
- `);await L.writeFile(c,u).catch(()=>{});let f=X.join(o,"MEMORY.md"),d=await L.readFile(f,"utf8").catch(()=>"");await L.writeFile(f,$c(d,l,s,a))}return{moved:t}}async function tp(){let t=[],e=[Me];try{for(let n of await L.readdir(aa(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=Un(n),o=await Oo(r),s=await $o(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await L.readFile(X.join(r,i),"utf8").catch(()=>""),{fm:l}=ia(c);a.push({file:i,name:an(l,"name")||i.replace(/\.md$/,""),description:an(l,"description")||"",scope:an(l,"scope")||null,modified:await he(X.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function ep(t,e){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(e)||e==="MEMORY.md")throw new Error("bad fact filename");let n=Un(t),r=X.join(n,e);if(!await L.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await L.rm(r,{force:!0}),await Ic(n,[[e,s]]);let a=X.join(n,"MEMORY.md"),i=await L.readFile(a,"utf8").catch(()=>"");await L.writeFile(a,i.split(`
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
39
  `).filter(l=>xr(l)!==e).join(`
40
- `)).catch(()=>{});let c=await sa(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function np(t){try{let e=await Rc(t),{harvested:n,changedByKey:r}=await Zf(t,e[0]||null);for(let[o,s]of r)await Oc(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import Dc from"node:fs/promises";import fp from"node:path";import ip from"node:fs/promises";import cp from"node:os";import lp from"node:path";import Mc from"node:fs/promises";import Bv from"node:path";var jv=0;async function Ft(t,e,{mode:n}={}){await Mc.mkdir(Bv.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++jv}.tmp`;await Mc.writeFile(r,JSON.stringify(e,null,2)+`
41
- `,n!=null?{mode:n}:void 0),await Mc.rename(r,t)}var Lv=1e4,Fv=3e4,rp=2e3,Uv=5,op=100,Hv=60*6e4,Wv=()=>process.env.BOSTRAT_STATE_DIR||lp.join(process.env.HOME||cp.tmpdir(),".bostrat"),up=()=>lp.join(Wv(),"content-outbox.json"),qv=()=>process.env.BOSTRAT_NODE_ID||cp.hostname(),Kv=(...t)=>fetch(...t),Gv=Fv,Vv=Hv;function ht(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Co=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function No(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),Lv);try{return await Kv(`${Qt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var la=null,Cc=null,ca=null,Mo=null;async function dp(){return la||(Cc||=ip.readFile(up(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>la={ops:Array.isArray(t?.ops)?t.ops:[],parked:Array.isArray(t?.parked)?t.parked:[]}).finally(()=>Cc=null),Cc)}async function ua(){await Ft(up(),la).catch(()=>{})}function sp(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function Pc(t){let e=await dp(),n=sp(t);if(t.op==="delete")e.ops=e.ops.filter(r=>!(r.family===t.family&&r.id===t.id)),e.ops.push(t);else{e.ops=e.ops.filter(o=>!(o.op==="delete"&&o.family===t.family&&o.id===t.id));let r=e.ops.findIndex(o=>sp(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>rp&&(e.ops=e.ops.slice(-rp)),await ua()}function ap(){ca||(ca=setTimeout(()=>{ca=null,vr().catch(()=>{})},Gv),ca.unref?.())}function Nc(t){let e=new Error(`plane ${t}`);return e.refused=!0,e}async function zv(t,e=process.env){if(t.op==="record"){let n=await No(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...Co(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:qv()}]})},e);if(!n.ok)throw Nc(n.status);let o=(await n.json().catch(()=>null))?.results?.[0];return o?.outcome==="rejected"?{delivered:!1,rejected:o.reason}:{delivered:!0}}if(t.op==="blob"){let n=await ip.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await No(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...Co(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw Nc(r.status);return r.status===400?{delivered:!1,rejected:(await r.json().catch(()=>null))?.error||"rejected"}:{delivered:!0}}if(t.op==="delete"){let n=await No(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:Co(e)},e);if(!n.ok)throw Nc(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function vr({env:t=process.env,log:e=console,now:n=Date.now()}={}){return ht(t)?Mo||(Mo=(async()=>{let r=await dp();Jv(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await zv(a,t)}catch(c){if(!c?.refused)return ap(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<Uv)return ap(),await ua(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};e.error?.(`[content-plane] ALARM parking ${a.op} ${a.family}/${a.id} after ${a.refusals} refusals (${c.message}) \u2014 draining the ${r.ops.length-1} ops behind it`),r.ops.shift(),r.parked.push({...a,parkedAt:n,error:c.message}),r.parked.length>op&&(r.parked=r.parked.slice(-op)),s++,await ua();continue}i.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await ua()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Mo=null}),Mo):{flushed:0,source:"unconfigured"}}function Jv(t,e){if(!t.parked.length||t.ops.length)return;let n=t.parked.filter(r=>e-(r.parkedAt||0)>=Vv);if(n.length){t.parked=t.parked.filter(r=>!n.includes(r));for(let r of n)t.ops.push({...r,refusals:0,parkedAt:void 0,error:void 0})}}async function Pt(t,e,{trackId:n=null,env:r=process.env}={}){return ht(r)?(await Pc({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),vr({env:r})):{queued:!1,source:"unconfigured"}}async function cn(t,e,n,r,o,{env:s=process.env}={}){return ht(s)?(await Pc({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),vr({env:s})):{queued:!1,source:"unconfigured"}}async function oe(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return ht(r)?(await Pc({op:"delete",family:t,id:e,deletedAt:n}),vr({env:r})):{queued:!1,source:"unconfigured"}}async function $t(t,{env:e=process.env}={}){if(!ht(e))return{records:[],source:"unconfigured"};try{let n=await No(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:Co(e)},e);if(!n.ok)throw new Error(`plane ${n.status}`);let r=await n.json();return{records:Array.isArray(r?.records)?r.records:[],source:"plane"}}catch(n){return{records:[],source:"unreachable",error:n?.message}}}async function da(t,e,n,r,{env:o=process.env}={}){if(!ht(o))return null;try{let s=await No(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Co(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="md"||r==="html"?a.toString("utf8"):a}catch{return null}}var Xv=".bostrat/handoff",Yv="latest.md",Zv=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null;async function pp(t,e,{box:n=null,agent:r=null}={}){if(!t||!ht())return{pushed:!1};try{let o=Date.now();return await Pt("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await cn("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function mp(t){try{let e=Zv(t);if(!e||!ht())return{restored:!1};let n=fp.join(t,Xv,Yv);if(await Dc.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await $t("checkpoints");if(o!=="plane")return{restored:!1};let s=r.find(i=>!i.deletedAt&&i.data?.id===e);if(!s?.data?.v)return{restored:!1};let a=await da("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Dc.mkdir(fp.dirname(n),{recursive:!0}),await Dc.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import Po from"node:fs/promises";import Qv from"node:os";import wp from"node:path";var tS=[/[A-Za-z0-9_-]{20,}#[A-Za-z0-9_-]{20,}/g,/\bsk-[A-Za-z0-9_-]{16,}/g,/\bghs_[A-Za-z0-9]{16,}|\bghp_[A-Za-z0-9]{16,}|\bgithub_pat_[A-Za-z0-9_]{20,}/g,/\bFlyV1 [A-Za-z0-9+/=_-]{16,}/g,/\bey[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g];function Hn(t){let e=String(t??"");for(let n of tS)e=e.replace(n,"\u2039redacted\u203A");return e}var Bc=process.env.BOSTRAT_DEBUG_DIR||wp.join(Qv.homedir(),".bostrat","debug"),Sr=wp.join(Bc,"handoff.jsonl"),hp=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function fa(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function eS(t){try{await Po.mkdir(Bc,{recursive:!0}),await Po.appendFile(Sr,`${JSON.stringify(t)}
42
- `,"utf8");let n=(await Po.readFile(Sr,"utf8")).split(`
43
- `).filter(Boolean);n.length>hp&&await Po.writeFile(Sr,`${n.slice(-hp).join(`
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(`
44
44
  `)}
45
- `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${Sr}: ${e?.message||e}`)}}async function q(t={}){let e={at:new Date().toISOString(),ts:Date.now(),...t},n=[e.flow||"handoff",e.phase||e.step||"?",e.trackId?`track=${e.trackId}`:"",e.sessionName?`box=${e.sessionName}`:"",e.error?`ERR=${e.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),e.sessionName&&fa())try{e.paneTail=Hn((await mt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(fa()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await eS(e)}async function gp({limit:t=80}={}){try{return(await Po.readFile(Sr,"utf8")).split(`
46
- `).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Hn(o.paneTail)),o}catch{return{raw:Hn(r)}}})}catch{return[]}}var yp={DEBUG_DIR:Bc,LOG_FILE:Sr};var ot=rS(nS);function _r(t){return new Promise(e=>setTimeout(e,t))}function pa(t,e){if(!e)return t;let n=o=>typeof o=="string"?o.split(e).join("<redacted>"):o,r=new Error(n(t?.message)||"command failed");return t?.code!==void 0&&(r.code=t.code),t?.stderr!==void 0&&(r.stderr=n(t.stderr)),r}var C=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";function kp(t){return`/tmp/tmux-${t}/default`}function sS({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=kp(r);return e?o:n?`${n}/bostrat/tmux-default`:o}var bp=typeof process.getuid=="function"?process.getuid():0,zt=sS({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return ma.existsSync(kp(bp))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:bp}),aS=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Lc=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",iS="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";function Fc(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${iS}=1`]}var Uc="bostrat-usage-probe",cS=new Set([Uc]);function Hc(t,e,n){let r=Wn(t),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);if(!r||o)return t;let s=`${t} --session-id ${e}`,a=/(^|\s)(--name|-n)(\s|=)/.test(t);return n&&!a&&wt(n)&&(s+=` --name ${n}`),s}function Wn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function ln(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function lS(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:Wn(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function xp(t,{loginShell:e=!1,failOpen:n=!1}={}){let r=await vp(t,{loginShell:e});return r===ha?!0:r===Tr?!1:n}var ha="present",Tr="absent",Ar="unknown",jc=5e3,uS=[250,1e3,3e3],dS=t=>new Promise(e=>setTimeout(e,t));async function vp(t,{loginShell:e=!1,timeoutMs:n=jc}={}){if(!t)return Tr;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await ot(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:n}),ha}catch(s){return s?.killed||s?.signal?Ar:s?.code===1||s?.code===127?Tr:Ar}}async function wa(t,{retries:e=uS,sleep:n=dS,probe:r=vp}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:jc});for(let a=0;s===Ar&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:jc*2});return s}var Wc={claude:"npm install -g @anthropic-ai/claude-code",codex:"npm install -g @openai/codex","cursor-agent":"curl https://cursor.com/install -fsS | bash",gh:"apt install gh (Debian: enable cli.github.com/packages) / brew install gh \u2014 see https://github.com/cli/cli#installation"};function P(...t){return["-S",zt,...t]}async function un(){if(!ma.existsSync(zt))return!1;try{return await ot(C,P("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function Do(){if(await un())return!0;try{ma.mkdirSync(zt.replace(/\/[^/]+$/,""),{recursive:!0}),await ot(C,P("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await un()}var fS=/^[A-Za-z0-9_.-]{1,64}$/;function wt(t){return typeof t=="string"&&fS.test(t)}async function Ce(){if(!ma.existsSync(zt))return[];try{let t="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}",{stdout:e}=await ot(C,P("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
47
- `).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d]=r.split("|"),p=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(p)?n-p*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null)}}).filter(r=>!cS.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function tt(t){return(await Ce()).find(n=>n.name===t)||null}var pS=/^[A-Za-z0-9 ._↻-]+$/,mS=64;function hS(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=mS&&pS.test(e)}function qc(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Sp(t){try{let{stdout:e}=await ot(C,P("list-sessions","-F","#{session_name}"));return e.split(`
48
- `).map(n=>n.trim()).includes(t)}catch{return!1}}var wS=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,gS=1e3,yS=/not found|Unknown command/i;function bS(t){let e=String(t||"").trim().split(/\s+/)[0]||"";if(e.includes("/"))return null;let n=[];for(let o=1;o<=e.length-3;o++)n.push(e.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function kS(t,e){let n=Date.now()+wS(),r=bS(e),o=!1,s=0;for(;;){if(await _r(gS),s+=1,!await Sp(t))return;let a=await mt(t,{lines:15}),i=!o&&yS.test(a)&&!!r?.test(a);if(!i&&s>1&&!await Er(t))return;if(i){o=!0,await q({flow:"spawn",phase:"launch-retry",sessionName:t,agent:ln(e),promptHead:e.slice(0,120)}).catch(()=>{}),await ot(C,P("send-keys","-t",t,"C-u")),await ot(C,P("send-keys","-t",t,"-l",e)),await ot(C,P("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await q({flow:"spawn",phase:"launch-failed",sessionName:t,agent:ln(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function qn(t,e=aS,n={}){if(!hS(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=qc(r);if(!wt(o))throw new Error("resulting tmux session name is invalid");if(!await Do())throw new Error(`host tmux server not running on ${zt} and auto-start failed \u2014 start one: ${C} -S ${zt} new-session -d -s keeper "sleep 2147483647"`);if(await Sp(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Lc,a=n.agent||ln(s),i=!!n.resumeSessionId,c=n.resumeSessionId||oS(),l=i?s:Hc(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&Wn(s),d=n.firstRun===!0;d||(f&&await on(e),a==="codex"&&await yr(e),a==="cursor"&&await br(e),await Qf(e),await mp(e));let p=f&&!d?ea():null,m=If(),h=["-e",`${Zs}=${m}`];p&&h.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&h.push(...Fc());try{await ot(C,P("new-session","-d","-s",o,"-c",e,...h))}catch(y){throw pa(p?pa(y,p):y,m)}return await Kn(o),await ot(C,P("set-option","-t",o,"@bostrat-session","1")),await ot(C,P("set-option","-t",o,"@bostrat-title",r)),await ot(C,P("set-option","-t",o,"@bostrat-sid",c)),await ot(C,P("set-option","-t",o,"@bostrat-agent",a)),await ot(C,P("send-keys","-t",o,l,"Enter")),lS(l)&&kS(o,l).catch(()=>{}),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function we(t){if(!wt(t))throw new Error("invalid session name");await ot(C,P("kill-session","-t",t))}async function Tp(t,e){if(wt(t))try{let n=e?P("set-option","-t",t,"@bostrat-sid",e):P("set-option","-u","-t",t,"@bostrat-sid");await ot(C,n)}catch{}}async function mt(t,{lines:e=40}={}){if(!wt(t))return"";try{let n=P("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await ot(C,n);return String(r||"")}catch{return""}}var xS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,vS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,_t=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,SS=Number(process.env.BOSTRAT_BOX_COLS)||120,TS=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function Ap(t){if(!wt(t))return 0;try{let{stdout:e}=await ot(C,P("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
49
- `).filter(Boolean).length}catch{return 0}}async function Kn(t,{cols:e=SS,rows:n=TS}={}){if(!wt(t))return!0;if(await Ap(t))return!1;try{let r=await _p(t);if(r.cols===e&&r.rows===n)return!0;await ot(C,P("resize-window","-t",t,"-x",String(e),"-y",String(n))),await ot(C,P("set-window-option","-t",t,"window-size","latest")),await _r(80)}catch{}return!0}async function AS(t){if(!wt(t))return 0;try{let{stdout:e}=await ot(C,P("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function Er(t){let e=await AS(t);if(!e)return!1;try{return await ot("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function _p(t){if(!wt(t))return{cols:0,rows:0};try{let{stdout:e}=await ot(C,P("display-message","-t",t,"-p","#{window_width} #{window_height}")),[n,r]=String(e||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function ga(t,{cols:e=xS,rows:n=vS}={}){if(wt(t)&&!await Ap(t))try{let r=await _p(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await ot(C,P("resize-window","-t",t,"-x",String(o),"-y",String(s))),await ot(C,P("set-window-option","-t",t,"window-size","latest")),await _r(80))}catch{}}async function dn(t){return await ga(t),mt(t,{lines:_t})}var _S=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function At(t,e,{settleMs:n=40,kind:r}={}){if(!wt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>_S.has(s));for(let s=0;s<o.length;s++)await ot(C,P("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await _r(n);return q({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function ya(t,e,{kind:n}={}){if(!wt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await ot(C,P("send-keys","-t",t,"-N",String(r),"BSpace")),q({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function ba(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!wt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await ot(C,P("send-keys","-t",t,"-l",s)),n>0&&await _r(n);q({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function Et(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!wt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await ot(C,P("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await _r(s),await ot(C,P("send-keys","-t",t,"Enter"))}q({flow:"tmux",phase:"send",sessionName:t,kind:o||"text",textLen:typeof e=="string"?e.length:0,textHead:typeof e=="string"?e.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}import ES from"node-pty";async function Ip(t,e,n={}){if(!wt(e)){ka(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await un()){ka(t,`host tmux server not running on ${zt}`),t.close(1011,"tmux server unavailable");return}if(!await tt(e)){ka(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=Ep(n.cols,120,Rp),o=Ep(n.rows,32,$p),s;try{s=ES.spawn(C,["-S",zt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ka(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Kc(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Kc(t,{type:"exit",code:a??null,signal:i??null}),t.close(1e3,"tmux exited"))}),t.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&OS(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),t.on("close",()=>{try{s.kill()}catch{}$S(e)})}var IS=6,RS=250;async function $S(t,e=IS,n=RS){for(let r=0;r<e;r++){if(await Kn(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Rp=20,$p=6;function OS(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=Rp&&Number(e)>=$p}function Ep(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Kc(t,e){try{t.send(JSON.stringify(e))}catch{}}function ka(t,e){Kc(t,{type:"error",message:e})}function Op(t){let e=typeof t=="string"&&t?t:"/",n=e.indexOf("?"),r=n===-1?e:e.slice(0,n),o=n===-1?"":e.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}import Bo from"node:fs";import MS from"node:fs/promises";import Ir from"node:path";import CS from"node:os";var Mp={"claude-opus-5":1e6,"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-5":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},NS=2e5,Gc=process.env.BOSTRAT_CLAUDE_PROJECTS||Ir.join(CS.homedir(),".claude","projects");function Vc(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function zc(t){let e=Ir.join(Gc,Vc(t)),n;try{n=Bo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Ir.join(e,o);try{let{mtimeMs:a}=Bo.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Ne(t,e){if(!e)return null;let n=Ir.join(Gc,Vc(t),`${e}.jsonl`);return Bo.existsSync(n)?n:null}function Cp(t){if(!t)return[];let e=Ir.join(Gc,Vc(t)),n;try{n=Bo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Bo.statSync(Ir.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Ut(t,e=131072){let n=await MS.open(t,"r");try{let{size:r}=await n.stat(),o=Math.max(0,r-e),s=r-o;if(s===0)return"";let a=Buffer.alloc(s);return await n.read(a,0,s,o),a.toString("utf8")}finally{await n.close()}}function PS(t){let e=t.split(`
50
- `);for(let n=e.length-1;n>=0;n--){let r=e[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function jo(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=Ne(t,e),o=!0,!r)return null}else if(n){if(r=zc(t),o=!1,!r)return null}else return null;let s;try{s=PS(await Ut(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Mp,s.model),i=Mp[s.model]||NS;return{source:"transcript",exact:o,model:s.model,windowKnown:a,window:i,occupancy:s.occupancy,pct:Math.min(1,s.occupancy/i),transcript:r}}function DS(t,e){return!t||!e?null:Ne(t,e)}function BS(t){return typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
51
- `).trim():""}function jS(t){if(!Array.isArray(t))return null;let e=t.filter(n=>n&&n.type==="tool_use").map(n=>n.name).filter(Boolean);return e.length?e:null}function LS(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Jc(t){let e=[],n=t?.message?.content;if(!Array.isArray(n))return e;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)e.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&e.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return e}function Np(t,e){if(!t.uuid)return null;let n=Jc(t).map((r,o)=>({uuid:t.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&e.get(r.toolUseId)||null}));return n.length?n:null}var FS=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Xc(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Ut(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
52
- `)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let h=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof h=="string"&&FS.test(h)&&s.set(m.id,h.split("/").pop())}if(LS(l.content)){let m=Np(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=BS(l.content),d=u==="assistant"?jS(l.content):null,p=u==="user"?Np(c,s):null;!f&&!d&&!p||o.push({role:u,text:f,tools:d||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var US=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function Pp(t,e,{turns:n=16}={}){let r=DS(t,e);if(!r)return"";let o=await Xc(r,{limit:n});return o.length?o.map(s=>{let a=s.role==="user"?"User":"Lead agent",i=s.tools?`_(also ran: ${s.tools.join(", ")})_`:"",c=s.images?.length?"(viewed an image)":"(took an action)";return`**${a}:** ${US(s.text||c,700)} ${i}`.trim()}).join(`
53
-
54
- `):""}import zA from"node:fs";import hn from"node:fs/promises";import wn from"node:path";import HS from"node:fs/promises";import{execFile as WS}from"node:child_process";import{promisify as qS}from"node:util";var Yc=qS(WS),KS=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Dp=new Set,Bp=Promise.resolve(),Zc=null;function Lp(t){Zc=typeof t=="function"?t:null}function GS(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function Fp({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||Dp.has(r)||(Dp.add(r),Bp=Bp.then(()=>VS({sessionName:t,from:e,to:r})).catch(()=>{}))}async function VS({sessionName:t,from:e,to:n}){await q({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Yc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await HS.realpath(r),s=GS(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Yc("npm",["install","-g","@openai/codex"],{env:a,timeout:KS});let i=(await Yc(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await q({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await jp({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await q({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await jp({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function jp(t){if(Zc)try{await Zc(t)}catch{}}var zS=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Up=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function JS(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
55
- `);if(!Up.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(zS);if(u)o.unshift({n:Number(u[1]),label:u[2]}),s=!0;else{if(s&&r[l].trim()==="")continue;if(s){var a=r[l].trim();break}}}if(o.length<2)return null;let i=o.sort((l,u)=>l.n-u.n);if(i.some((l,u)=>l.n!==u+1))return null;let c=typeof a=="string"&&Up.test(a)?a:"The agent is asking for approval";return{provider:e,box:n,promptId:`${e}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}var xa=/(^|\n)\s*›/,qp=/^\s*Welcome to Codex\b/;function Rr(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
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
56
  `);for(let r=n.length-1;r>=0;r--)if(qp.test(n[r]))return n.slice(r).join(`
57
- `);return e}function XS(t){let e=String(t).trim();if(!e)return!1;let n=(e.match(/[A-Za-z]/g)||[]).length;return n>=3&&n/e.length>=.5}function YS(t){return/device code/i.test(t)?"works from the device you're reading this on":/sign in with chatgpt/i.test(t)?"opens a browser link on the node itself \u2014 can't finish from another device":""}var Hp=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function ZS(t,e=""){let n=Rr(t);if(xa.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
58
- `),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Hp);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let p=r[d].trim();if(p){if(Hp.test(r[d])||/^Press enter/.test(p))break;f=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:f})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!qp.test(r[l])&&XS(u)&&i.push(u)}let c=i.join(" ").slice(0,400)||"Codex isn't signed in on this box \u2014 choose a sign-in method";return{provider:"codex",box:e,multiSelect:!1,cursorIndex:Math.max(0,a.findIndex(l=>l.cursor)),answerMode:"keys",promptId:`codex:signin:${a.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Sign in",multiSelect:!1,options:a.map(l=>{let u=YS(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Wp=/Enter this one-time code/,QS=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function tT(t,e=""){let n=Rr(t);if(xa.test(n)||!Wp.test(n))return null;let r=n.split(`
59
- `),o=r.findIndex(i=>Wp.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(QS);if(c){s=c[1];break}}if(!s)return null;let a=(n.match(/https:\/\/\S+/)||[])[0]||"https://auth.openai.com/codex/device";return{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:`codex:device-code:${s}`,questions:[{question:`Codex is waiting for you to finish signing in: open ${a} and enter code ${s} (expires in 15 minutes). The code goes straight to OpenAI \u2014 bostrat never sees your login. This card clears once OpenAI confirms.`,header:"Sign in",multiSelect:!1,options:[{label:"Cancel sign-in",description:"Go back to the sign-in options"}]}]}}function eT(t,e=""){let n=Rr(t);return xa.test(n)||!/open the following link to authenticate/.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-browser",questions:[{question:"Codex started its browser sign-in, which opens on the box itself and can't complete from another device. Go back and choose \u201CSign in with Device Code\u201D instead.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function nT(t,e=""){let n=Rr(t);return xa.test(n)||!/Paste or type your API key/.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-apikey",questions:[{question:"Codex is asking for an OpenAI API key. For safety a key can only be typed in the Terminal tab, never through chat. To use your ChatGPT subscription instead, go back and pick a sign-in option.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}var Qc=/Run Everything|Add a follow-up|Plan, search, build anything/;function rT(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||Qc.test(n))return null;let r=n.replace(/\r/g,"").split(`
60
- `).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}var oT=/click this link to log in|Signing in with the browser/;function sT(t,e=""){let n=String(t);if(!oT.test(n)||Qc.test(n))return null;let r=n.replace(/\r/g,"").split(`
61
- `),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function aT(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||Qc.test(n)?null:{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,promptId:"cursor:signin-qr",questions:[{question:"Cursor is showing a QR sign-in code in the Terminal tab \u2014 scan it from another device to log in. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function Kp(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=tT(e,n)||eT(e,n)||nT(e,n)||ZS(e,n);if(r)return r}if(t==="cursor"){let r=sT(e,n)||aT(e,n)||rT(e,n);if(r)return r}return JS(e,{provider:t,box:n})}function Gp(t,e){let n=Array.isArray(e?.[0])?e[0]:[],o=(Number.isInteger(n[0])&&n[0]>=0?n[0]:0)+1;return String(o)}var iT=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,tl=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Lo=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Ot={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key/},cursor:{up:/Run Everything|Add a follow-up|Plan, search, build anything/,busy:/ctrl\+c to stop|^\s*[⠁-⣿]+\s+\S/m,block:/Workspace Trust Required|Trust this workspace|\[a\]\s*Trust|Trusting workspace/,auth:/Press any key to log in|click this link to log in|Scan this QR code to log in|Signing in with the browser/}},cT=/Trust this workspace|\[a\]\s*Trust/,lT=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function uT(t){let e=String(t||"");if(!Ot.codex.block.test(e))return null;let n=Rr(e);if(!Ot.codex.block.test(n))return null;let r=n.match(lT);return{from:r?.[1]||null,to:r?.[2]||null}}function fn(t){return new Promise(e=>setTimeout(e,t))}var dT=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,va=new Map;function Vp(t,e){let n=Date.now(),r=dT();for(let[s,a]of va)n-a>=r&&va.delete(s);let o=`${t}::${e}`;return va.has(o)?!1:(va.set(o,n),!0)}var fT=68,pT=/^\s*[─━]{10,}\s*$/;function mT(t){let e=0;for(let n of String(t).split(`
62
- `))pT.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function el(t,e){let n=Ot[t];if(!n?.busy)return{state:"idle",reason:null};if(rl(t,e))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(e))return{state:"idle",reason:null};if(n.busy.test(e))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(e))return{state:"busy",reason:null};let r=mT(e);return t==="claude"&&r>0&&r<fT?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function nl(t,e){return el(t,e).state}var hT=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,wT=15;function Jp(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
- `).slice(-wT).join(`
64
- `);return hT.test(n)}function Fo(t,e){return nl(t,e)==="busy"}function Sa(t,e){return nl(t,e)!=="idle"}function Ta(t,e){let n=Ot[t];return n?.up?n.up.test(e):!0}function gT(t,e){let n=Ot[t];return n?rl(t,e)?!1:n.up.test(e)&&nl(t,e)==="idle":!0}function rl(t,e){let n=Ot[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="codex"?!0:!n.up.test(e):!1:!1}async function zp(t,e,n){let r=Date.now()+n,o=0;for(await Kn(t).catch(()=>{});;){let s=await mt(t);if(gT(e,s)){await fn(Lo);return}if(e==="cursor"&&cT.test(s)&&!Ot.cursor.up.test(s)&&o<3){if(!Vp(t,"cursor-trust")){await fn(tl);continue}o+=1,await q({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await Et(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await fn(Lo);continue}if(e==="codex"&&o<3){let a=uT(s);if(a){if(!Vp(t,`codex-update:${a.from}->${a.to}`)){await fn(tl);continue}o+=1,await q({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await Et(t,"3",{enter:!1,kind:"codex-update-skip"}),Fp({sessionName:t,from:a.from,to:a.to}),await fn(Lo);continue}}if(Date.now()>r){if(rl(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Ot.codex.auth.test(s)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":e==="cursor"&&Ot.cursor.auth.test(s)?a="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":e==="claude"&&Ot.claude.auth.test(s)?a="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":e==="claude"&&Ot.claude.block.test(s)&&(a='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),q({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await Er(t)){let a=`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw q({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),q({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Ot[e]?.block?.test(s),paneUp:!!Ot[e]?.up?.test(s),paneBusy:!!Ot[e]?.busy?.test(s)}).catch(()=>{}),await fn(Lo);return}await fn(tl)}}function Xp({agent:t,agentCmd:e}={}){let n=e?ln(e):ln(t||"");return{label:n,realClaude:e?Wn(e):n==="claude",realCodex:e?/codex/.test(e)&&!/fake-codex|fake-lead/.test(e):n==="codex",realCursor:e?/cursor/.test(e)&&!/fake-cursor|fake-box/.test(e):n==="cursor"}}function Aa({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Xp({agent:n,agentCmd:r});return!e||!o&&!s?null:pn({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function pn({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=iT()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Xp({agent:r,agentCmd:o});if(i&&t&&await on(t),c&&t&&await yr(t),l&&t&&await br(t),(i||c||l)&&n){await zp(n,a,s);return}let u=Ot[r]?r:Ot[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await zp(n,u,s);return}await fn(Lo)}var yT=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),bT=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function Pe(t){return t==="codex"?{enter:!0,submitDelayMs:yT}:t==="claude"?{enter:!0,submitDelayMs:bT}:{enter:!0}}import Ea from"node:fs/promises";import nm from"node:path";var kT=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],xT={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope out\b/,/\btalk through\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/]};function Gn(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of kT){let o=0;for(let s of xT[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var vT=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),ST=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Yp(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))vT.has(r)&&(e+=o||0),ST.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Uo(t){return String(t||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var Zp=1.5,Qp=.75,TT=new Set("a an and are as at be been but by for from had has have he her his i if in into is it its of on or our so than that the their them then there these they this those to was were will with would you your we".split(" "));function em(t){let e=Uo(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||TT.has(r)||n.push(r);return n}function AT(t){let e=[];for(let n of t||[]){let r=n.payload||{},o=String(r.text||"").trim();if(n.type==="turn.user"){if(n.source==="hook"||!o)continue;e.push(tm(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(tm(n,"assistant",o))}}return e}function tm(t,e,n){let r=Gn(n);return{id:t.id,seq:t.seq,ts:t.payload?.ts??t.ts,role:e,boxId:t.boxId??null,provider:t.provider??null,text:n,tags:r?[r.category]:[]}}function Ho(t){let e=AT(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=em(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=e.length;return{chunks:e,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function _a(t,e,n={}){let{k:r=8,role:o=null,tag:s=null,boxId:a=null}=n;if(!t||!t.N)return[];let i=[...new Set(em(e))];if(!i.length)return[];let c=new Map;for(let u of i){let f=t.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(t.N-d+.5)/(d+.5));for(let[m,h]of f){let y=t.lengths[m],k=h+Zp*(1-Qp+Qp*(y/(t.avgdl||1))),v=p*(h*(Zp+1)/k);c.set(m,(c.get(m)||0)+v)}}let l=[];for(let[u,f]of c){let d=t.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var rm="Relevant prior discussion (verbatim, retrieved";async function _T(t){let e=nm.join(t,".bostrat","events"),n;try{n=await Ea.readdir(e)}catch{return Ho([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Ea.readFile(nm.join(e,o),"utf8");for(let a of s.split(`
65
- `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Ho(r)}function ET(t){let e=String(t||""),n=a=>{let i=e.match(a);return i?i[1].trim():""},r=n(/#+\s*Objective\s*\n([\s\S]*?)(?:\n#+\s|$)/i),o=n(/#+\s*Next steps?[^\n]*\n([\s\S]*?)(?:\n#+\s|$)/i);return`${r}
66
- ${o}`.trim()||e.slice(0,2e3)}function IT(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=_a(t,e,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),f=u.length>800?u.slice(0,800)+"\u2026":u,d=`- **${l}:** ${f}`;if(i+d.length>r||(a.push(d),i+=d.length,a.length>=n))break}return a.length?["",`## ${rm}, not a summary)`,"_Auto-selected from the Track's event log by lexical relevance to the Objective / Next steps. Verbatim excerpts, not re-summarization (docs/memory-segmentation-indexing-scope.md \xA71.3)._","",...a,""].join(`
67
- `):""}async function om(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Ea.readFile(e,"utf8");if(a.includes(rm))return{appended:!1,reason:"already-enriched"};let i=await _T(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=IT(i,ET(a),{k:r,maxChars:o,exclude:s});return c?(await Ea.appendFile(e,`
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,`
68
68
  `+c),{appended:!0,chars:c.length,chunks:c.split(`
69
- - `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}import ol from"node:fs/promises";import $r from"node:path";import RT from"node:os";import{EventEmitter as $T}from"node:events";import{randomUUID as OT}from"node:crypto";var MT=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","context.usage","cost.recorded","memory.seed","handoff.artifact","code.pushed","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","changelog.published","stall.detected","error"]),CT=new Set(["user","agent","orchestrator","system","peer"]),NT=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Ht=new $T;Ht.setMaxListeners(0);var sl=process.env.BOSTRAT_STATE_DIR||$r.join(process.env.HOME||RT.tmpdir(),".bostrat");function Ra(t){return t?.cwd?$r.join(t.cwd,".bostrat","events"):$r.join(sl,"events")}var sm=(t,e)=>$r.join(t,`${e}.ndjson`),Ia=new Map;async function PT(t,e){if(!Ia.has(e)){let o=(await B(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);Ia.set(e,o)}let n=Ia.get(e)+1;return Ia.set(e,n),n}function DT(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function $(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!MT.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!CT.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!NT.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?$r.join(sl,"events"):Ra(t),o=e.epoch;if(o==null&&typeof t=="object"&&e.boxId&&Array.isArray(t?.boxes)){let c=t.boxes.find(l=>l.name===e.boxId);o=DT(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await PT(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||OT(),trackId:n,boxId:e.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:e.ts??Date.now(),type:e.type,actor:e.actor??"system",provider:e.provider??null,source:e.source??"orchestrator",payload:e.payload??{},...e.causationId?{causationId:e.causationId}:{},...e.correlationId?{correlationId:e.correlationId}:{}};return await ol.mkdir(r,{recursive:!0}),await ol.appendFile(sm(r,n),JSON.stringify(i)+`
70
- `),Ht.emit("event",i),Ht.emit(`track:${n}`,i),i}async function B(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?$r.join(sl,"events"):Ra(t));let o;try{o=await ol.readFile(sm(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
71
- `))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function am(t,e){let n=`track:${t}`;return Ht.on(n,e),()=>Ht.off(n,e)}function BT(t){let e=[],n="",r=null;for(let o=0;o<t.length;o++){let s=t[o];if(r){n+=s,s===r&&t[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&t[o+1]==="&"){e.push(n),n="",o++;continue}if(s==="|"&&t[o+1]==="|"){e.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
72
- `){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function Wo(t){let e=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}e.push(n),n="",o=!1}};for(let a=0;a<t.length;a++){let i=t[a];if(r){i===r&&t[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&t[a+1]==="<")return s(),e.push("<<"),e;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),e}function qo(t){let e=t;for(;e.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(e[0]);)e=e.slice(1);for(;e.length&&(e[0]==="sudo"||e[0]==="time"||e[0]==="env");)e=e.slice(1);return e}var Mr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Mt=t=>t&&!/[$`]/.test(t)?t:null,gt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Or=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},lm=t=>t.startsWith("-"),im=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function ut(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(lm(o)){e.has(o)&&r++;continue}n.push(o)}return n}var jT={status:()=>"Check git status",diff:()=>"Review changes",log:()=>"Review commit history",show:()=>"Inspect a commit",add:()=>"Stage files",commit:()=>"Commit changes",push:()=>"Push to remote",pull:()=>"Pull from remote",fetch:()=>"Fetch from remote",merge:()=>"Merge branches",rebase:()=>"Rebase branch",stash:()=>"Stash changes",clone:t=>t[1]?`Clone ${gt(t[1])}`:"Clone repository",checkout:()=>"Switch branches",switch:()=>"Switch branches",restore:()=>"Restore files",reset:()=>"Reset git state",branch:(t,e)=>e.includes("--show-current")?"Check current branch":"List branches",remote:()=>"Check git remotes","rev-parse":()=>"Resolve a git ref",worktree:()=>"Manage git worktrees","merge-base":()=>"Find the merge base","ls-files":()=>"List tracked files"},LT={pr:t=>({create:"Create a pull request",list:"List pull requests",view:"View a pull request",merge:"Merge a pull request",checks:"Check PR status"})[t[1]]||"Run a gh pr command",auth:()=>"Authenticate GitHub CLI",api:()=>"Call the GitHub API",repo:()=>"Inspect the GitHub repo",issue:()=>"Work with GitHub issues"};function um(t){let e=t.indexOf("-n"),n=e!==-1?t[e+1]:null;if(!n)return null;let r=/^(\d+),(\d+)p$/.exec(n)||/^(\d+)p$/.exec(n);return r?r[2]?{from:r[1],to:r[2]}:{from:r[1],to:r[1]}:null}function FT(t){let e=um(t),n=Mt(ut(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${gt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${gt(n)}`:t.includes("-i")&&n?`Edit ${gt(n)}`:null}function al(t){if(t.includes("--files")){let s=Mt(ut(t,im)[0]);return s&&s!=="."?`List files in ${Or(s)}`:"List files"}let e=ut(t,im),n=e[0];if(!n)return null;let r=Mt(e.slice(1).find(s=>s!==".")),o=Mr(n,40);return r?`Search for \u201C${o}\u201D in ${Or(r)}`:`Search for \u201C${o}\u201D`}function il(t){let e=ut(t,new Set(["--prefix"])),n=e[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?e[1]?`Run ${e[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function cl(t,e){if(t.includes("-e")||t.includes("--eval")||t.includes("-c")||t[1]==="-"||t.includes("<<"))return`Run an inline ${e} script`;if(t.includes("--test"))return"Run tests";let n=ut(t)[0];return n?`Run ${gt(n)}`:null}function UT(t){let e=ut(t),n=e[0],r=e.find(a=>/\.(service|timer|socket|target)$/.test(a))||e[1],o=r?r.replace(/\.service$/,""):"a service";return{status:`Check ${o} status`,show:`Inspect ${o}`,restart:`Restart ${o}`,start:`Start ${o}`,stop:`Stop ${o}`,"daemon-reload":"Reload systemd"}[n]||null}function cm(t){return t.find(e=>/^https?:\/\//.test(e))||null}var HT={git:t=>{let e=ut(t,new Set(["-C","-c"])),n=jT[e[0]];return n?n(e,t):null},gh:t=>{let e=ut(t),n=LT[e[0]];return n?n(e):null},sed:FT,rg:al,grep:al,egrep:al,nl:t=>{let e=Mt(ut(t)[0]);return e?`Read ${gt(e)}`:null},cat:t=>{let e=Mt(ut(t)[0]);return e?`Read ${gt(e)}`:null},head:t=>{let e=Mt(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${gt(e)}`:null},tail:t=>{let e=Mt(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${gt(e)}`:null},wc:t=>{let e=Mt(ut(t)[0]);return e?`Count lines in ${gt(e)}`:"Count input lines"},ls:t=>{let e=Mt(ut(t)[0]);return e&&e!=="."?`List files in ${Or(e)}`:"List files"},find:t=>Mt(t[1])&&!lm(t[1])&&t[1]!=="."?`Find files in ${Or(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Mt(ut(t)[0]);return e?`Create directory ${Or(e)}`:null},touch:t=>{let e=Mt(ut(t)[0]);return e?`Create ${gt(e)}`:null},rm:t=>{let e=Mt(ut(t)[0]);return e?`Delete ${Or(e)}`:null},mv:t=>{let e=Mt(ut(t)[0]);return e?`Move ${gt(e)}`:null},cp:t=>{let e=Mt(ut(t)[0]);return e?`Copy ${gt(e)}`:null},chmod:t=>{let e=Mt(ut(t)[1]);return e?`Change permissions on ${gt(e)}`:null},diff:t=>{let e=ut(t);return e.length>=2?`Compare ${gt(e[0])} and ${gt(e[1])}`:null},curl:t=>{let e=cm(t);return e?`Fetch ${Mr(e,56)}`:"Fetch a URL"},wget:t=>{let e=cm(t);return e?`Fetch ${Mr(e,56)}`:"Fetch a URL"},npm:il,pnpm:il,yarn:il,npx:t=>{let e=ut(t)[0];return e?`Run ${e}`:null},node:t=>cl(t,"Node"),python:t=>cl(t,"Python"),python3:t=>cl(t,"Python"),make:t=>{let e=ut(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:UT,journalctl:t=>{let e=t.findIndex(r=>r==="-u"||r==="--unit"||r==="--user-unit"),n=e!==-1?t[e+1]:null;return n?`Read ${n.replace(/\.service$/,"")} logs`:"Read system logs"},tmux:t=>{let e=ut(t,new Set(["-S","-L","-t","-f"]))[0];return{ls:"List tmux sessions","list-sessions":"List tmux sessions","kill-session":"Kill a tmux session","send-keys":"Send keys to a tmux session","capture-pane":"Capture a tmux pane","new-session":"Start a tmux session"}[e]||null},ps:()=>"List processes",which:t=>{let e=ut(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=ut(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},WT=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function qT(t){let e=qo(Wo(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(gt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return ll(e[2]);let n=gt(e[0]);if(WT.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=HT[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function ll(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
73
- `,n);i!==-1&&(e=e.slice(0,i))}let r=BT(e);for(let i=0;i<r.length-1;i++){let c=qo(Wo(r[i])),l=qo(Wo(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(gt(c[0]))&&gt(l[0])==="sed"){let u=um(l),f=ut(c)[0];if(u&&f)return Mr(u.from===u.to?`Read line ${u.from} of ${gt(f)}`:`Read lines ${u.from}\u2013${u.to} of ${gt(f)}`,88)}}let o=[];for(let i of r){let c=qT(i);c&&o.push({d:c,head:gt(qo(Wo(i))[0]||""),sub:ut(qo(Wo(i)))[0]})}if(!o.length)return null;let s=o.find(i=>i.head==="git"&&i.sub==="commit"),a=o.find(i=>i.head==="git"&&i.sub==="push");return s&&a?"Commit and push changes":Mr(s?s.d:o[0].d,88)}var dl=new Set(["Bash"]);function Cr(t){let e=t.split(`
74
- `).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function mn(t,e=""){let n=e.trim();return n||ll(t)||Cr(t)}function ul(t,e){if(t){if(Array.isArray(t)){for(let n of t)ul(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&dl.has(t.name)){let n=String(t.input?.command||"").trim();n&&e.push({id:t.id||null,tool:t.name,command:n,description:String(t.input?.description||"").trim()})}for(let n of Object.values(t))ul(n,e)}}}async function dm(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Ut(t,e)}catch{return[]}let o=[];for(let s of r.split(`
75
- `)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(ul(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:mn(f.command,f.description),ts:u})}return o.slice(-n)}function fm(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,h)=>h>=s&&(m.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let p=d.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:p,ts:Math.max(...p.map(m=>m.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}import pm from"node:fs";import pl from"node:fs/promises";import fl from"node:path";import KT from"node:os";var GT=process.env.BOSTRAT_CODEX_SESSIONS||fl.join(process.env.CODEX_HOME||fl.join(KT.homedir(),".codex"),"sessions");async function mm(t){try{return await pl.realpath(t)}catch{return t}}function VT(t=GT){let e=[],n=r=>{let o;try{o=pm.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=fl.join(r,s.name);if(s.isDirectory())n(a);else if(s.isFile()&&s.name.startsWith("rollout-")&&s.name.endsWith(".jsonl")){let i=/-([0-9a-f-]{36})\.jsonl$/i.exec(s.name);try{e.push({id:i?i[1]:null,file:a,mtimeMs:pm.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function zT(t,e=1048576){let n;try{n=await pl.open(t,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(e,65536)),o="",s=0;for(;;){let{bytesRead:a}=await n.read(r,0,r.length,s);if(!a)break;o+=r.toString("utf8",0,a);let i=o.indexOf(`
76
- `);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function JT(t){let e=await zT(t);if(!e)return null;let n;try{n=JSON.parse(e)}catch{return null}return n?.type==="session_meta"&&n?.payload?.cwd||null}async function Vn(t,e){if(!t&&!e)return null;let n=VT();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await mm(t);for(let o of n){let s=await JT(o.file);if(s&&(s===t||await mm(s)===r))return o}return null}var XT=new Set(["output_text","text"]),YT=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&XT.has(e.type)).map(e=>e.text).join(`
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
77
  `).trim():"";async function hm(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Vn(t,e);if(!o)return[];let s;try{s=await Ut(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
78
- `)){let p=d.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let v=m.payload?.type;v==="task_started"?(l=!0,u=!1,f=m.payload?.turn_id||null):v==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:hl,sourceId:m.payload?.turn_id||f||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let h=m.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,ml.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let y=YT(h.content),k=c.length?c.slice():null;c=[],!(!y&&!k)&&a.push({role:"assistant",text:y,tools:k||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}var Ko=new Set(["exec_command","shell","exec"]),ml=new Set(["wait","write_stdin"]),hl="Codex ended the turn without a response \u2014 it hit an error the CLI shows only in the terminal (e.g. quota or billing). Check the Terminal tab.";function ZT(t){if(typeof t?.cmd=="string"&&t.cmd.trim())return t.cmd.trim();let e=t?.command;return typeof e=="string"&&e.trim()?e.trim():Array.isArray(e)&&e.length?e.length===3&&/^(ba)?sh$/.test(e[0])&&e[1]==="-lc"?String(e[2]).trim():e.map(String).join(" ").trim():null}function QT(t){let e=t[0];if(e!=='"'&&e!=="'"&&e!=="`")return null;let n="";for(let r=1;r<t.length;r++){let o=t[r];if(o==="\\"){let s=t[++r];if(s===void 0)return null;if(s==="n")n+=`
79
- `;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function tA(t){let e=String(t||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(e);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=e.slice(s,a+1<n.length?n[a+1]:e.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=QT(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function wm(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Vn(t,e);if(!o)return[];let s;try{s=await Ut(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
80
- `)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let f=[];if(u.type==="function_call"&&Ko.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=ZT(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=tA(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,m)=>{a.push({id:d?m?`${d}:${m}`:d:null,tool:u.name,command:p,description:"",summary:mn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function gm(t,e){let n=await Vn(t,e);if(!n)return null;try{return Math.round((await pl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var ym=/^\s*─{8,}\s*$/,eA=/^❯[  ]?(.*)$/;function Go(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
81
- `).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(ym.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(ym.test(n[c])){o=c;break}if(o<0)return null;let s=n.slice(o+1,r);if(!s.length)return null;let a=s[0].match(eA);if(!a)return null;let i=[a[1].trim(),...s.slice(1).map(c=>c.trim())].filter(Boolean);return i.length?i.join(" "):null}function vt(t){return String(t||"").replace(/\s+/g," ").trim()}function bm(t,e,{slackMs:n=2e4}={}){let r=vt(e?.text);return!r||typeof e?.at!="number"?!1:(Array.isArray(t)?t:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=e.at-n&&vt(o.text).includes(r))}function km(t,e,n,{busy:r=!1}={}){let o=Go(t,n);return o&&wl(e,o)?"stuck":r||o?"submitted":"unknown"}function wl(t,e){let n=vt(t),r=vt(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var nA=/^This session is being continued from a previous conversation that ran out of context\./,vm=["Primary Request and Intent","Key Technical Concepts","Files and Code Sections","Errors and Fixes","Problem Solving","All user messages","Pending Tasks","Current Work","Optional Next Step"],gl=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${vm.join("|")})[:*\t ]*$`,"gim"),rA=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,oA=420,sA=[{h:"What we were doing",from:["Primary Request and Intent"]},{h:"Key decisions",from:["Key Technical Concepts"]},{h:"Where it got to",from:["Current Work","Problem Solving"]},{h:"Left to do",from:["Pending Tasks","Optional Next Step"]}],aA=116,yl=168;function iA(t){return nA.test(String(t||"").trimStart())}function Sm(t){let e=String(t||"");if(!iA(e.trimStart()))return null;let n=e.replace(rA,"").trim(),r=cA(n),o=[];for(let u of sA){let f=u.from.map(d=>Nr(r.get(d))).find(d=>d&&!xm(d));f&&o.push({h:u.h,text:Vo(f,oA)})}let s=Nr(r.get("Current Work"))||Nr(r.get("Primary Request and Intent"))||uA(n),a=Oa(s),i=Vo($a(bl(a[0]||"")),aA)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Vo($a(bl(l)),yl));for(let u of["Pending Tasks","Optional Next Step"]){let f=Oa(Nr(r.get(u)))[0]||"";if(!(!f||xm(f))){c.push(Vo(`Left to do: ${$a(bl(f))}`,yl));break}}if(!c.length){let u=Oa(Nr(r.get("Problem Solving"))||"")[0];u&&c.push(Vo($a(u),yl))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:e.length}}function cA(t){let e=new Map,n=[];gl.lastIndex=0;for(let r=gl.exec(t);r;r=gl.exec(t))n.push({name:lA(r[1]),start:r.index,end:r.index+r[0].length});for(let r=0;r<n.length;r++){let o=n[r+1]?.start??t.length;e.has(n[r].name)||e.set(n[r].name,t.slice(n[r].end,o).trim())}return e}function lA(t){return vm.find(n=>n.toLowerCase()===t.toLowerCase())||t}function Nr(t){return t?String(t).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
82
- `).map(e=>e.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(e=>e&&!/^#{1,6}[ \t]/.test(e)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function uA(t){let e=t.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Nr(e.split(/\n{2,}/).slice(0,2).join(" "))}function Oa(t){let e=String(t||"").trim();if(!e)return[];let r=e.replace(/\b([A-Za-z0-9_-]+)\.(js|ts|tsx|mjs|json|md|css|jsonl|sh|yml|yaml|py|html)\b/g,"$1<DOT>$2").replace(/\b(e\.g|i\.e|etc|vs|approx|no|fig|Dr|Mr|Ms|St)\./gi,"$1<DOT>").split(/(?<=[.!?])["')\]]?\s+(?=[A-Z"“'(\[]|\d)/).map(s=>s.replace(/<DOT>/g,".").trim()).filter(Boolean),o=[];for(let s of r){let a=o[o.length-1];a&&(a.match(/["“”]/g)?.length??0)%2===1?o[o.length-1]=`${a} ${s}`:o.push(s)}return o}function bl(t){return String(t||"").replace(/^(?:Immediately before (?:this|the) summary(?: request)?[,:]?\s*|At the (?:time|point) of (?:this )?compaction[,:]?\s*|Currently[,:]?\s*|The (?:most recent|immediately preceding|current) work (?:was|is)\s*)/i,"").replace(/^(?:I(?:'m|'d| am| was| had| have)?\s+(?:currently\s+|just\s+)?(?:been\s+)?(?:working on|in the middle of|completing|finishing)\s+|The user(?:'s)?\s+(?:most recent\s+)?(?:request|ask) (?:was|is)\s+(?:to\s+)?)/i,"").replace(/^I(?:'m|'d| am| was| had| have)?\s+(?:just\s+|then\s+)?(?=(?:[a-z]+(?:ed|ing)|ran|wrote|built|made|began|found|left|sent|took|went|did|got|kept|held|brought|caught|chose|came|saw|said|gave|told|set|put|read)\b)/,"").replace(/^["“]|["”]$/g,"").replace(/^(\w)/,e=>e.toUpperCase()).trim()}function $a(t){return String(t||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,e=>`\u2026/${e.split("/").slice(-2).join("/")}`)}function xm(t){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(t||"").trim())}function Vo(t,e){let n=String(t||"").trim();if(n.length<=e)return n;let r=Oa(n),o="";for(let i of r){let c=o?`${o} ${i}`:i;if(c.length>e)break;o=c}if(o.length>=Math.min(60,e*.4))return o;let s=n.slice(0,e),a=s.lastIndexOf(" ");return`${(a>e*.6?s.slice(0,a):s).replace(/[,;:.\s]+$/,"")}\u2026`}var dA=/^Base directory for this skill:\s*(\S+)/,fA=/^Run the "([^"]+)" workflow\./,pA=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,mA=/^<local-command-(?:stdout|caveat)>/i,hA=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,wA=/^Continue from where you left off\.$/,gA=/^<task-notification>/i,yA=/<summary>([\s\S]*?)<\/summary>/i,bA=/<task-id>([\s\S]*?)<\/task-id>/i,kA=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,xA=/<status>([\s\S]*?)<\/status>/i,vA=/<result>([\s\S]*?)(?:<\/result>|$)/i,Tm=600,kl=new Set(["model","clear","compact","login","exit","quit"]);function Am(t){return kl.has(String(t||"").replace(/^\//,"").toLowerCase())}var SA=/^\/([a-z0-9:_-]+)$/i;function Ma(t){let e=String(t||"").trim();if(!e)return null;let n=Sm(e);if(n)return n;let r=e.match(dA);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=e.match(fA);if(o)return{kind:"skill",name:o[1]};let s=e.match(pA);if(s){let i=s[1].toLowerCase();return kl.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=e.match(SA);if(a&&kl.has(a[1].toLowerCase()))return{kind:"suppress"};if(mA.test(e))return{kind:"suppress"};if(wA.test(e))return{kind:"suppress"};if(hA.test(e))return{kind:"suppress"};if(gA.test(e)){let i=l=>TA(e.match(l)?.[1].trim()||"")||null,c=i(vA);return{kind:"task",summary:i(yA)||"",taskId:i(bA),toolUseId:i(kA),status:i(xA)?.toLowerCase()||null,resultHead:c?c.length>Tm?`${c.slice(0,Tm).trimEnd()}\u2026`:c:null}}return null}function TA(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function _m(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":t?.kind==="resume-summary"?t.title?`Context resumed from summary: ${t.title}`:"Context resumed from summary":""}function Em(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":t?.kind==="resume-summary"?"context-resumed":"command-run"}import AA from"node:crypto";var xl=new Set(["cursor","codex"]),Rm=4,_A=4,EA=2,IA=24,Im=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,zo=t=>typeof t=="string"?t.trim():"";function RA(t){let e=Array.isArray(t)?t:Array.isArray(t?.questions)?t.questions:null;if(!e||!e.length)return null;let n=[];for(let r of e.slice(0,Rm)){let o=zo(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,_A)){let c=zo(i?.label);if(!c)return null;let l=zo(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<EA)return null;n.push({question:o,header:zo(r?.header).slice(0,IA),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function vl(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;Im.lastIndex=0;let e=null;for(let o;o=Im.exec(t);)e=o;if(!e||e.index+e[0].length<t.trimEnd().length)return null;let n;try{n=JSON.parse(e[2])}catch{return null}let r=RA(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function Sl(t,e,n){let r=AA.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Tl(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=Rm}={}){if(!Array.isArray(t)||!e||!xl.has(n))return null;let s=null;for(let l=t.length-1;l>=0;l--){let u=t[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==e||s.agent!==n)return null;let a=s.ask?.questions||vl(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Sl(n,s.sourceId||s.id,a);return typeof r=="function"&&r(c)?null:{provider:n,box:e,promptId:c,groupId:c,answerMode:"say",source:"transcript",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function $m(t,e,n){let r=[];(t?.questions||[]).forEach((a,i)=>{let c=(e?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=zo(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
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:
83
83
  ${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
84
84
  `)}`:s=o,r.length&&o&&(s+=`
85
85
  ${o}`),s||""}var Om=`
@@ -102,79 +102,79 @@ your turn. The operator sees the questions as an interactive card in their chat
102
102
  answer. Never use the block for rhetorical questions, progress updates, or
103
103
  anything you can decide yourself; keep asking in prose for questions without
104
104
  enumerable options.
105
- `;var rt=t=>t.payload?.ts??t.ts;function $A(t){return{id:t.id,kind:"prompt",groupId:t.groupId,provider:t.provider,box:t.box,agent:t.provider,questionTotal:t.questions.length,answeredVia:"chat",questions:t.questions.map((e,n)=>({index:n,header:e.header||"",question:e.question,multiSelect:!!e.multiSelect,chosen:[]})),ts:t.ts}}var OA=new Set(["opened","ci-fixed","reconciled","pushed"]),Mm=t=>`${t.repo??""}#${t.number}`;function MA(t,e,n){let r=Mm(n),o=t.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of t){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?t.get(o):t.get(s);if(o&&o!==s){t.delete(o);let i=e.get(o);if(e.delete(o),i?.length){let c=e.get(s)??[];e.set(s,c.concat(i))}}return t.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function zn(t){let e=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,f=new Map;for(let g of t||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let w=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(w))for(let A of w.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let E=A[0].split("/").pop().toLowerCase();f.has(E)||f.set(E,[]),f.get(E).push(g.payload?.ts??g.ts)}}let d=3*6e4,p=20*6e4,m=(g,w)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some(E=>E>=w-d&&E<=w+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let w=vt(g.payload?.text);w&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(w))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let w=vt(g.payload?.text);w&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(w))}else if(g.type==="job.started"&&g.payload?.id)a.add(g.payload.id),(g.payload.kind||"agent")==="agent"&&g.payload.name&&c.add(g.payload.name);else if(g.type==="turn.user"&&g.payload?.note?.kind==="task"){let w=g.payload.note,A={status:w.status||null,summary:w.summary||null,resultHead:w.resultHead??null,ts:rt(g)};w.toolUseId&&i.set(w.toolUseId,A);let E=String(w.summary||"").match(u);E&&l.set(E[1],A)}let h=new Map,y=new Map,k=null,v=new Map,I=new Map,R=new Map,z=new Map,O=new Map,M=new Map,S=new Map,N=()=>{for(let g of S.values())g.questions.length&&e.push(g);S.clear()},D=null,it=6e4,ct=new Map,T=(g,w)=>{let A=o.get(g);if(!A?.length)return!1;let E=vt(w),J=s.get(g);return A.some(W=>{if(!E.startsWith(W))return!1;let at=vt(E.slice(W.length));return!!at&&!!J?.has(at)})};for(let g of t||[]){let w=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(w.text||"").trim()&&!w.images?.length)break;{let A=w.note||Ma(w.text);if(A){if(A.kind==="task"){if(A.toolUseId&&a.has(A.toolUseId))break;let E=String(A.summary||"").match(u);if(!A.toolUseId&&E&&c.has(E[1]))break}if(A.kind==="command"&&Am(A.command))break;if(A.kind==="resume-summary"){e.push({id:g.id,kind:"resume-summary",title:A.title||"Context resumed from summary",bullets:A.bullets||[],sections:A.sections||[],rawChars:A.rawChars??null,ts:rt(g),box:g.boxId,agent:g.provider});break}A.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:Em(A),name:A.name??null,command:A.command??null,summary:A.summary??null,ts:rt(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${rt(g)}`)||g.source==="transcript"&&T(g.boxId,w.text))break;if(w.kind==="merge-notify"||w.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:w.kind==="ship-notify"?"ship-notified":"merge-notified",text:w.text,ts:rt(g),box:g.boxId,agent:g.provider});break}if(w.kind==="mock-picked"||w.kind==="ci-fix"||w.kind==="reconcile"||w.kind==="pr-go"||w.kind==="push-go"||w.kind==="spin-up-view"||w.kind==="prompt-answer"){w.kind==="prompt-answer"&&(D=null);break}for(let A of v.values())A.status="stale";v.clear(),D&&D.box===g.boxId&&(e.push($A(D)),D=null),e.push({id:g.id,kind:"message",role:"user",text:w.text,ts:rt(g),box:g.boxId,agent:g.provider,boxRole:w.boxRole,...g.actor==="orchestrator"?{orchestration:w.kind||!0}:{},...w.sourceId?{sourceId:w.sourceId}:{},...w.attachments?.length?{attachments:w.attachments}:{},...w.images?.length?{images:w.images}:{}});break}case"peer.message":{let A=w.from||{};e.push({id:g.id,kind:"event",type:"peer-message",text:w.text,from:A.trackName||A.boxId||A.name||null,fromTrackId:A.trackId||null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(w.model==="<synthetic>"&&/^No response requested\.$/.test(String(w.text||"").trim()))break;N();let A=(w.images||[]).filter(at=>!m(at,rt(g)));if(!String(w.text||"").trim()&&!A.length)break;if(g.provider==="cursor"){let at=`${g.boxId}|${w.text}`;if(n.has(at))break;n.add(at)}(!D||D.box===g.boxId)&&(D=null);let E=g.provider==="codex"?(w.tools||[]).filter(at=>!ml.has(at)&&!Ko.has(at)):w.tools||[],J=w.text,W=null;if(xl.has(g.provider)){let at=vl(String(w.text||""));at&&(J=at.text,W={questions:at.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:J,ts:rt(g),box:g.boxId,agent:g.provider,boxRole:w.boxRole,...W?{ask:W}:{},...E.length?{tools:E}:{},...w.model?{model:w.model}:{},...w.sourceId?{sourceId:w.sourceId}:{},...A.length?{images:A}:{}}),W&&(D={id:g.id,groupId:Sl(g.provider,w.sourceId||g.id,W.questions),provider:g.provider,box:g.boxId,questions:W.questions,ts:rt(g)});break}case"job.started":{if(!w.id)break;let A=rt(g),E=i.get(w.id);if(!E&&(w.kind||"agent")==="agent"&&w.name){let Tt=l.get(w.name);Tt&&Tt.ts>=A&&(E=Tt)}let J={jobId:w.id,jobKind:w.kind||"agent",name:w.name||null,detail:w.detail||null,status:E?E.status&&E.status!=="completed"?"failed":"done":"running",startedTs:A,finishedTs:E?.ts??null,summary:E?.summary??null,resultHead:E?.resultHead??null},W=ct.get(g.boxId),at=W?.jobs[W.jobs.length-1]?.startedTs;if(W&&at!=null&&A-at<=it)W.jobs.push(J);else{let Tt={id:g.id,kind:"job-dock",dockId:w.id,ts:A,box:g.boxId,agent:g.provider,jobs:[J]};ct.set(g.boxId,Tt),e.push(Tt)}break}case"handoff.done":e.push(ge(g,"lead-handoff"));break;case"subtask.delegated":e.push({...ge(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(ge(g,"subtask-merged"));break;case"pr.state.changed":{if(!w.change)break;if(w.change==="opened"){for(let E of v.values())E.status="stale";v.clear()}let A=ge(g,`pr-${w.change}`);if(A.box=g.boxId??null,OA.has(w.change)&&w.number!=null){let E=MA(h,y,w);if(h.size>=2){for(let W of y.values())for(let at of W)at.grouped=!0;A.groupPrs=[...h.values()];let J=[A];for(let W of h.keys())y.set(W,J)}else{let J=y.get(E)??[];J.push(A),y.set(E,J)}}else if(w.change==="merged"||w.change==="closed"||w.change==="lost"){let E=Mm(w),J=k&&!k.complete||h.size>=2;if(h.has(E))h.delete(E),y.delete(E);else if(w.number!=null)for(let W of[...h.keys()])W.endsWith(`#${w.number}`)&&(h.delete(W),y.delete(W));if(J&&w.number!=null){(!k||k.complete)&&(k={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:rt(g),ts:rt(g),box:g.boxId,agent:g.provider},e.push(k)),k.prs.push({number:w.number,url:w.url??null,title:w.title??null,repo:w.repo??null,branch:w.branch??null,change:w.change,ts:rt(g)}),k.remaining=[...h.values()],k.complete=h.size===0;break}}e.push(A);break}case"pr.offered":{if(!w.offerId)break;for(let E of v.values())E.status="stale";v.clear();let A={id:g.id,kind:"pr-offer",offerId:w.offerId,excerpt:w.excerpt||null,branch:w.branch||null,base:w.base||null,offered:Array.isArray(w.offered)&&w.offered.length?w.offered:null,toPr:w.toPr&&typeof w.toPr=="object"?{number:w.toPr.number??null}:null,repos:Array.isArray(w.repos)&&w.repos.length?w.repos:null,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};v.set(w.offerId,A),I.set(w.offerId,A),e.push(A);break}case"pr.offer.answered":{let A=I.get(w.offerId);A&&(A.status=w.action||"dismiss",A.actions=Array.isArray(w.actions)?w.actions:null,A.notify=w.notify===!0,v.delete(w.offerId));break}case"box.state.changed":{let A=w.change==="model"?"model-change":w.change;if(!A)break;let E=ge(g,A);A==="model-change"&&(E.to=w.model),e.push(E);break}case"view.state.changed":{if(w.change!=="created"||w.restarted)break;e.push({id:g.id,kind:"view-card",viewId:w.viewId,name:w.name,slug:w.slug,port:w.port,origin:w.origin,ts:rt(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:w.name||null,reason:w.reason||"no-view",ts:rt(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(w.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:w.mockId,name:w.name,group:w.group??null,version:w.version??null,siblings:w.siblings??[],ts:rt(g),box:g.boxId,agent:g.provider});break}if(w.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:w.mockId,name:w.name,group:w.group??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(w.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:w.flowId,name:w.name,stepCount:w.stepCount??null,mockIds:w.mockIds??[],ts:rt(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(w.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:w.runbookId,name:w.name,ts:rt(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:w.provider||"linear",key:w.key,url:w.url,title:w.title??null,linkSource:w.linkSource??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"ticket.move.offered":{if(!w.offerId)break;for(let E of R.values())E.key===w.key&&(E.status="stale",R.delete(E.offerId));let A={id:g.id,kind:"ticket-offer",offerId:w.offerId,provider:w.provider||"linear",key:w.key,url:w.url??null,phase:w.phase??null,from:w.from??null,to:w.to?.name??null,prNumber:w.prNumber??null,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};R.set(w.offerId,A),z.set(w.offerId,A),e.push(A);break}case"ticket.move.answered":{let A=z.get(w.offerId);A&&(A.status=w.action==="moved"?"moved":"dismiss",R.delete(w.offerId));break}case"ticket.state.changed":{for(let A of R.values())A.key===w.key&&A.to===w.to&&(A.status="stale",R.delete(A.offerId));if(w.mode==="offer")break;e.push(ge(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:w.key,url:w.url??null,excerpt:w.excerpt??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(ge(g,"ticket-flow"));break;case"ticket.create.offered":{if(!w.proposalId)break;for(let E of O.values())E.status="stale";O.clear();let A={id:g.id,kind:"ticket-create-offer",proposalId:w.proposalId,provider:w.provider||"linear",epic:w.epic??null,issues:Array.isArray(w.issues)?w.issues:[],team:w.team??null,needsTeam:!!w.needsTeam,linkCurrentTrack:!!w.linkCurrentTrack,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};O.set(w.proposalId,A),M.set(w.proposalId,A),e.push(A);break}case"ticket.create.answered":{let A=M.get(w.proposalId);A&&(A.status=w.action==="created"?"created":"dismiss",A.createdCount=w.createdCount??null,A.failedCount=w.failedCount??null,O.delete(w.proposalId));break}case"ticket.created":{let A={items:Array.isArray(w.items)?w.items:[],failed:Array.isArray(w.failed)?w.failed:[],team:w.team??null},E=w.proposalId?M.get(w.proposalId):null;if(E){E.created=A;break}e.push({id:g.id,kind:"ticket-created",...A,ts:rt(g),box:g.boxId,agent:g.provider});break}case"error":{if(!w.kind)break;e.push(ge(g,w.kind));break}case"usage.cap.hit":e.push(ge(g,"usage-cap-hit"));break;case"usage.cap.warning":e.push(ge(g,"usage-cap-warning"));break;case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:w.provider||g.provider,ts:rt(g),at:rt(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(D=null,w.control||!w.question)break;let A=w.groupId||w.promptId;if(!A)break;let E=S.get(A);E||(E={id:g.id,kind:"prompt",groupId:A,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:w.questionTotal??1,questions:[],ts:rt(g)},S.set(A,E));let J=w.questionIndex??0,W={index:J,header:w.header||"",question:w.question,multiSelect:!!w.multiSelect,chosen:Array.isArray(w.chosen)?w.chosen:[]},at=E.questions.findIndex(Tt=>Tt.index===J&&vt(Tt.question)===vt(W.question));at>=0?E.questions[at]=W:E.questions.push(W),E.questions.sort((Tt,Xs)=>Tt.index-Xs.index),E.questionTotal=Math.max(E.questionTotal,w.questionTotal??1),E.questions.length>=E.questionTotal&&(e.push(E),S.delete(A));break}default:break}}return NA(e.sort((g,w)=>(g.ts??0)-(w.ts??0)))}var CA=2*6e4;function NA(t){let e=new Map,n=[];for(let r of t){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=e.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=CA){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};e.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else e.delete(r.box),n.push(r)}return n}function Al(t,e=null){let n=[];for(let o of t||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:PA(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(e??zn(t)).filter(o=>o.kind==="message");return fm(n,r)}function PA(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Cr(t.command)?e:mn(t.command,t.description||"")}function ge(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:rt(t),ts:rt(t)}}var DA=12e4,BA=new Set(["archived","unarchived","opened"]);function Cm(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+DA,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&BA.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var jA=2500,LA=new Set(["pr-opened","pr-merged","pr-auto-armed"]);function Nm(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||!LA.has(r.type)||!r.box)continue;let o=(r.ts??0)+jA,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var FA=12,UA=2e3,Pm=48e3,HA=30,WA=10;function qA(t){let e=null,n=null,r=null;for(let o of t??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(e={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:e,lastPush:n,lastEventAt:r}}var KA=(t,e)=>{let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1)}\u2026`},Dm=(t,e)=>t&&e?Math.max(1,Math.round((t-e)/6e4)):null;function GA(t){for(let e=(t?.length??0)-1;e>=0;e--){let n=t[e];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function VA(t,e){let n=[],r=[],o=new Set,s=new Set;for(let a=(t?.length??0)-1;a>=0;a--){let i=t[a];if(i?.type!=="tool.called")continue;if(e!=null&&(i.ts??0)<=e)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<HA&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<WA&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}var Bm={failover:"**This is a reconstruction, not a checkpoint.** The node this Track lived on went offline without a clean handoff; bostrat assembled this brief from the durable event log. Treat every claim below as a lead, not a fact \u2014 **verify the repository state first** (git status, git log, the diff against the last pushed ref) before continuing work.","in-place":"**This is a reconstruction, not a checkpoint.** The previous lead ended without writing one (out of credits, capped, or unresponsive); bostrat assembled this status from the Track's event log. You are in the SAME live workspace \u2014 uncommitted work is present locally. **Run git status and git log first** and treat every claim below as a lead, not a fact."};function jm(t,{trackName:e="this Track",now:n=null,mode:r="failover"}={}){let o=qA(t),s=GA(t),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=zn(t??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-FA);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${e}`),l.push(""),l.push(Bm[r]??Bm.failover),l.push(""),o.lastPush?.sha){let d=Dm(n,o.lastPush.ts),p=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let d=Dm(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=VA(t??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${KA(d.text,UA)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
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
106
  `);return f.length<=Pm?f:`${f.slice(0,Pm)}
107
107
 
108
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
109
 
110
- [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var Gm="Checkpoint: write a work-in-progress handoff",Vm="HANDOFF WRITTEN",zm="Resume from .bostrat/handoff/latest.md",JA="/clear",XA=t=>`${Gm} to the EXACT absolute path ${t} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file) so a FRESH Claude context with none of this conversation can resume seamlessly. Use these sections: Objective; Done (with file paths); In progress (exact state); Next steps (ordered); Key files & commands; Open decisions/blockers; Resume instruction (one line). Be concrete and self-contained \u2014 assume the reader has none of this chat. When the file is written, reply only with ${Vm} and take no further action.`,YA=`${zm}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Jm="Beat: write a",Xm="BEAT WRITTEN",ZA=".bostrat/arc",QA="beat.txt",Fm=200,Ym=4,Zm=5,t_=36,e_=t=>`${Jm} short summary of what this workstream accomplished, to the EXACT absolute path ${t} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file). Format the file as: FIRST LINE = one sentence of at most 18 words, plain past tense, saying the outcome \u2014 no heading, no bullet, no trailing list. THEN, only if this workstream covered several separable pieces of work, up to ${Ym} further lines, each starting with "- " and naming ONE piece in at most ${Zm} words \u2014 a label, not a sentence (e.g. "- stale working flag on poll"). Write nothing else in the file. When the file is written, reply only with ${Xm} and take no further action.`,Um=/^(?:[-–—*•·]|\d+[.)])\s+/,n_=(t,e,n)=>{let r=t.split(/\s+/).filter(Boolean).slice(0,e).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};function r_(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Um.test(s)){if(r.length>=Ym)continue;let a=n_(s.replace(Um,"").replace(/[.;,:]+$/,""),Zm,t_);a&&r.push(a)}else r.length||n.push(s);let o=n.join(" ").replace(/\s+/g," ").replace(/[:\-–—]+$/,"").trim();if(!o)throw new Error("beat file has no summary line");return o.length>Fm&&(o=`${o.slice(0,Fm-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Qm(t,e,{timeoutMs:n=9e4}={}){let r=wn.join(e,ZA),o=wn.join(r,QA);await hn.mkdir(r,{recursive:!0});let s=await Ca(o),a=await tt(t);if(!a)throw new Error(`session "${t}" not found`);return await Na(t,a,e_(o)),await ch(o,s,n),r_(await hn.readFile(o,"utf8"))}function th(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Vm||n===Xm:n.startsWith(Gm)||n===JA||n.startsWith(zm)||n.startsWith(Jm):!1}var o_=6e5;function eh(t){let e=[];for(let r of t?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-o_;e.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&e.push({box:r.to,from:s,to:o})}let n=t?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&e.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&e.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),e}function nh(t,e){let n=[];for(let r of t?.boxes||[]){let o=e?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function rh(t,{windows:e=[]}={}){if(th(t.role,t.text))return!0;if(t.role!=="assistant"||!t.ts)return!1;for(let n of e)if(t.box===n.box&&t.ts>=n.from&&t.ts<=n.to)return!0;return!1}var s_=3500,a_=3e5,_l=new Map;function Pa(t){return _l.get(t)||null}async function oh(t){let e=_l.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await tt(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);let r={name:t,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:wn.join(n.cwd,Wm,qm)};return _l.set(t,r),i_(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function i_(t,e){t.step="summarize",await sh(t.name,e),t.step="clear";let n=await tt(t.name);await Na(t.name,n,"/clear"),await lh(s_),t.step="resume",await Na(t.name,n,YA),t.step="done",t.finishedAt=Date.now()}async function sh(t,e,{timeoutMs:n=a_}={}){let r=wn.join(e,Wm),o=wn.join(r,qm);await hn.mkdir(r,{recursive:!0});let s=await Ca(o),a=await tt(t);await q({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:o,beforeMtime:s}),await Na(t,a,XA(o)),await ch(o,s,n),await q({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:o,afterMtime:await Ca(o)});let i=await om(e,o,{exclude:u=>th(u.role,u.text)});await q({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(c){await $({id:c,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await hn.readFile(o,"utf8");await $({id:c,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Km(u),ts:Date.now()}})}catch{}}await np(e);let l=await c_(r,o);return c&&await pp(c,l||o,{box:t,agent:a?.agent??null}),o}async function ah(t,{timeoutMs:e}={}){let n=await tt(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);return sh(t,n.cwd,e?{timeoutMs:e}:void 0)}var Hm=".bostrat/handoff/status-latest.md";async function ih(t,e,{trackName:n="this Track"}={}){let r=await B({id:t,cwd:e}),o=jm(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=wn.join(e,Hm);return await hn.mkdir(wn.dirname(s),{recursive:!0}),await hn.writeFile(s,o,"utf8"),await $({id:t,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Km(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Hm}async function Ca(t){try{return(await hn.stat(t)).mtimeMs}catch{return 0}}async function ch(t,e,n){let r=Date.now()+n;for(;;){if(await Ca(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await lh(2e3)}}async function c_(t,e){try{if(!zA.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=wn.join(t,`${n}.md`);return await hn.copyFile(e,r),r}catch{return null}}function lh(t){return new Promise(e=>setTimeout(e,t))}async function Na(t,e,n){if(e){let o=e.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await pn({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await tt(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await Et(t,n,{kind:"orchestration-drive",...Pe(r.agent)})}import F from"node:fs/promises";import U from"node:path";import{randomUUID as Ms}from"node:crypto";import{execFile as eO}from"node:child_process";import{promisify as nO}from"node:util";import Zw from"node:path";var El={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},uh={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},l_=/^\d{4}-\d{2}-\d{2}$/;function dh(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!l_.test(String(t)))throw new Error(`pricing.js: ${n} window ${e} must be YYYY-MM-DD, got ${JSON.stringify(t)}`);let r=Date.parse(`${t}T${e==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${e} is not a real date: ${t}`);return r}function u_(t){let e={};for(let[n,r]of Object.entries(t)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);e[n]=r.map(o=>({...o,fromMs:dh(o.from,"from",n),toMs:dh(o.to,"to",n)}))}return e}var d_=u_(El);function f_(t){if(t==null)return Date.now();if(typeof t=="number")return Number.isFinite(t)?t:null;if(t instanceof Date){let n=t.getTime();return Number.isFinite(n)?n:null}let e=Date.parse(String(t));return Number.isFinite(e)?e:null}function p_(t){if(!t)return null;let e=String(t).trim();return El[e]?e:uh[e]?uh[e]:Object.keys(El).find(r=>e.startsWith(r))||null}function fh(t,e){let n=p_(t);if(!n)return null;let r=f_(e);return r===null?null:d_[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}var ph=(t,e)=>fh(t,e)!==null;function gn(t,e={},n){let r=fh(t,n);return r?((e.input||0)*r.input+(e.output||0)*r.output+(e.cacheWrite||0)*r.cacheWrite+(e.cacheRead||0)*r.cacheRead)/1e6:0}var mh=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function m_(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function hh(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function De(t){let e=t.payload?.model||"unknown",n=m_(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=t.source==="vendor",s=r,a=!1;if(n&&!o){let l=gn(e,n,t.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!ph(e,t.ts);return{usd:s,model:e,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function ye(t,{boxLabels:e={}}={}){let n=(t||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=mh();for(let d of n){let{usd:p,model:m,tokens:h,repriced:y,unpriced:k,asRecorded:v}=De(d);i+=v;let I=d.provider||"unknown",R=d.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(m)),a+=p,hh(f,h),r[m]||={usd:0,tokens:mh(),events:0},r[m].usd+=p,r[m].events+=1,hh(r[m].tokens,h),o[I]||={usd:0,events:0},o[I].usd+=p,o[I].events+=1,s[R]||={usd:0,events:0,provider:I,models:new Set,label:e[R]||R},s[R].usd+=p,s[R].events+=1,m!=="unknown"&&s[R].models.add(m),d.epoch!=null){let z=(s[R].epochs||={})[d.epoch]||={usd:0,events:0,provider:I,models:new Set};z.usd+=p,z.events+=1,z.provider=I,m!=="unknown"&&z.models.add(m)}}return{totalUsd:Pr(a),asRecordedUsd:Pr(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Da(r,d=>({...d,usd:Pr(d.usd)})),byProvider:Da(o,d=>({...d,usd:Pr(d.usd)})),byBox:Da(s,d=>({usd:Pr(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Da(d.epochs,p=>({usd:Pr(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Pr=t=>Math.round(t*1e4)/1e4,Da=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import kt from"node:fs/promises";import St from"node:path";import yh from"node:os";import{randomUUID as y_}from"node:crypto";import{execFile as b_}from"node:child_process";import{promisify as k_}from"node:util";import{chromium as x_}from"playwright-core";import Il from"node:os";import{execFile as h_}from"node:child_process";import{promisify as w_}from"node:util";var g_=w_(h_);async function Ba(){if(process.platform!=="darwin")return Il.freemem();try{let{stdout:t}=await g_("vm_stat",[],{timeout:3e3}),e=Number(/page size of (\d+) bytes/.exec(t)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(t)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*e;return r>0?r:Il.freemem()}catch{return Il.freemem()}}var Ol=k_(b_),bh=process.env.BOSTRAT_STATE_DIR||St.join(process.env.HOME||yh.tmpdir(),".bostrat"),kh=St.join(bh,"mocks.json"),v_=process.env.BOSTRAT_MOCK_ENGINE||"chromium",S_=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",T_=process.env.BOSTRAT_FIREFOX_BIN||"firefox",A_=1300,Dr=2,__=32,xh=1500,Br=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,E_=700*1024*1024,wh=100*1024*1024,I_=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",R_=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",vh=2e4,se=class extends Error{constructor(e,n=""){super(n?`${e}
110
+ [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var 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
111
  ${n.trim().split(`
112
112
  `).slice(-6).join(`
113
- `)}`:e),this.name="MockRenderError"}},Jn=null,Rl=null;async function xe(){return Jn||(Rl||=(async()=>{try{Jn=JSON.parse(await kt.readFile(kh,"utf8"))}catch{Jn={mocks:[]}}finally{Rl=null}return Jn.mocks||=[],Jn})(),Rl)}async function te(){await Ft(kh,Jn)}var be=t=>Pt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),ke=(t,e,n)=>{let r=(t.versions||[]).find(s=>s.v===e),o=n==="mp4"||n==="webm"?Jo(t,r,"video"):r&&Jo(t,r,n)||(n==="html"?ee(t,e).html:null);return o?cn("mocks",t.id,e,n,o).catch(()=>{}):Promise.resolve()};function Sh(t,e){ke(t,e.v,e.videoRel?.endsWith(".webm")?"webm":"mp4"),e.posterRel&&ke(t,e.v,"png")}function Dt(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...t.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}var Xo=t=>St.join(t,".bostrat","mocks");function ee(t,e){let n=Xo(t.cwd);return{html:St.join(n,`${t.id}.v${e}.html`),png:St.join(n,`${t.id}.v${e}.png`)}}function Jo(t,e,n){if(!e)return null;let r=n==="html"?e.htmlRel:n==="png"?e.pngRel??e.posterRel??null:n==="video"?e.videoRel??null:null;return r?St.join(t.cwd,r):null}function ae(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Ml(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var $_=Buffer.from([137,80,78,71,13,10,26,10]);function ja(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals($_)}function O_(t){return!Buffer.isBuffer(t)||t.length<32?null:t.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:t.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function M_(t){let e=Buffer.alloc(24),n=await kt.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function C_({imgW:t,imgH:e,bbox:n,pad:r,align:o=1}){if(!n||!(t>0)||!(e>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=t*.9&&a>=e*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function N_(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,h)=>{r.onload=m,r.onerror=()=>h(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let h=m*o*4,y=0;y<o;y++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(y<u&&(u=y),y>d&&(d=y),m<f&&(f=m),p=m);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},e.toString("base64"))}async function P_(t,e,{bg:n,crop:r}){let o=await t.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((f,d)=>{c.onload=f,c.onerror=()=>d(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function D_({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await x_.launch({executablePath:S_,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new se("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Dr})).newPage();a.setDefaultTimeout(Br),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Br}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Br});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await N_(a,i),u=l&&C_({...l,pad:__*Dr,align:Dr});u&&(i=await P_(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<xh)throw new se(`capture produced a ${i.length}-byte image`);let c=Ml(i);if(!c)throw new se("output is not a PNG");return await kt.writeFile(e,i),{width:Math.round(c.width/Dr),height:Math.round(c.height/Dr),dpr:Dr,renderMs:Date.now()-r}}catch(s){throw s instanceof se?s:new se(/timeout/i.test(s?.message||"")?`render timed out after ${Br}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function gh({htmlPath:t,outPath:e,width:n}){let r=await kt.mkdtemp(St.join(yh.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Ol(T_,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Br,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new se(c?.killed?`render timed out after ${Br}ms`:"firefox exited non-zero",s)}let a=await kt.stat(e).catch(()=>null);if(!a||a.size<xh)throw new se(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Ml(await M_(e));if(!i)throw new se("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await kt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function B_(t){if(v_==="firefox")return gh(t);try{return await D_(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return gh(t);throw e}}var j_=B_;var $l=Promise.resolve();function Th(t){return $l=$l.then(t,t),$l}async function Ah(t,e){let n=await xe(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await te(),await Dt(r,"rendering",{version:e});try{let o=await Ba();if(o<E_)throw new se(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=ee(r,e),a=await j_({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await kt.rm(s.png,{force:!0}).catch(()=>{});return}Cl(r,{v:e,source:"html",htmlRel:St.relative(r.cwd,s.html),pngRel:St.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await te(),await Dt(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),be(r),ke(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await te(),await Dt(r,"error",{version:e,error:r.error}),be(r)}}}function Cl(t,e){t.versions=(t.versions||[]).filter(r=>r.v!==e.v).concat(e),t.versions.sort((r,o)=>r.v-o.v);let n=t.versions[t.versions.length-1];t.readyVersion=n.v,t.height=n.height,t.dpr=n.dpr,t.renderMs=n.renderMs??null,n.v===t.version&&(t.state="ready",t.error=null)}function L_({trackId:t,name:e,cwd:n,width:r}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?A_:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Xn({trackId:t,group:e}={}){let n=await xe(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function Be(t){let n=(await xe()).mocks.find(r=>r.id===t);return n?{...n}:null}function _h({html:t,image:e,video:n}){if([typeof t=="string"&&t.trim(),!!e,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(e&&!ja(e))throw new Error("image must be a PNG");if(n){let o=O_(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>wh)throw new Error(`video must be ${wh/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function Eh({trackId:t,name:e,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=L_({trackId:t,name:e,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=_h({html:n,image:r,video:o}),m=await xe(),h={id:y_().slice(0,8),trackId:t,box:c||null,agent:l||null,name:e.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:f,height:null,dpr:null,state:"queued",source:d?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await kt.mkdir(Xo(s),{recursive:!0}),m.mocks.push(h),d?(await kt.writeFile(ee(h,1).html,n),await te(),await Dt(h,"created"),be(h),ke(h,1,"html"),Th(()=>Ah(h.id,1))):o?(await Rh(h,1,o,p),await te(),await Dt(h,"created"),await Dt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),be(h),Sh(h,h.versions[0])):(await $h(h,1,r),await te(),await Dt(h,"created"),await Dt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),be(h),ke(h,1,"png")),{...h}}async function Ih(t,{html:e,image:n,video:r,width:o}={}){let a=(await xe()).mocks.find(u=>u.id===t);if(!a)throw new Error("mock not found");let i=typeof e=="string"&&e.trim(),c=_h({html:e,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await kt.mkdir(Xo(a.cwd),{recursive:!0}),i?(a.state="queued",await kt.writeFile(ee(a,l).html,e),await te(),await Dt(a,"updated",{version:l}),be(a),ke(a,l,"html"),Th(()=>Ah(a.id,l))):r?(await Rh(a,l,r,c),await te(),await Dt(a,"updated",{version:l}),await Dt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:ae(a)?.durationS??null}),be(a),Sh(a,a.versions.find(u=>u.v===l))):(await $h(a,l,n),await te(),await Dt(a,"updated",{version:l}),await Dt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),be(a),ke(a,l,"png")),{...a}}async function F_(t){try{let{stdout:e}=await Ol(I_,["-v","error","-print_format","json","-show_format","-show_streams",t],{timeout:vh,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(e),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function U_(t,e){try{await Ol(R_,["-y","-v","error","-i",t,"-frames:v","1","-f","image2",e],{timeout:vh,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await kt.stat(e).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Rh(t,e,n,r){let o=Xo(t.cwd),s=St.join(o,`${t.id}.v${e}.${r.ext}`),a=St.join(o,`${t.id}.v${e}.poster.png`);await kt.writeFile(s,n);let i=await F_(s),c=await U_(s,a);Cl(t,{v:e,source:"video",htmlRel:null,pngRel:null,videoRel:St.relative(t.cwd,s),posterRel:c?St.relative(t.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(t.width=i.width)}async function $h(t,e,n){let r=Ml(n);if(!r)throw new Error("image must be a PNG");let o=ee(t,e);await kt.writeFile(o.png,n),Cl(t,{v:e,source:"image",htmlRel:null,pngRel:St.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Oh(t){let e=await xe(),n=e.mocks.find(o=>o.id===t);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Dt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Mh(t){let e=await xe(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=ee(r,o);await kt.rm(s.html,{force:!0}).catch(()=>{}),await kt.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await kt.rm(St.join(r.cwd,s),{force:!0}).catch(()=>{});return await te(),await Dt(r,"deleted"),oe("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Ch(t){let n=(await xe()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=St.join(bh,"mock-archive",t.id),o=Xo(t.cwd),s=St.join(r,".bostrat","mocks");await kt.mkdir(s,{recursive:!0});let a=await kt.readdir(o).catch(()=>[]);for(let i of a)await kt.copyFile(St.join(o,i),St.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await kt.stat(St.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=t.name||t.id,i.updatedAt=Date.now(),be(i)}return await te(),n.length}async function Nh(t){let e=await xe(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await te();for(let r of n)oe("mocks",r.id).catch(()=>{})}return n.length}async function Ph(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await xe();for(let r of n.mocks){await be(r);for(let o of r.versions||[])o.htmlRel&&await ke(r,o.v,"html"),(o.pngRel||o.posterRel)&&await ke(r,o.v,"png"),o.videoRel&&await ke(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}import Zo from"node:fs/promises";import Wt from"node:path";import H_ from"node:os";import W_ from"node:net";import{randomUUID as q_}from"node:crypto";import{execFile as K_}from"node:child_process";import{promisify as G_}from"node:util";var Se=G_(K_),V_=process.env.BOSTRAT_STATE_DIR||Wt.join(process.env.HOME||H_.tmpdir(),".bostrat"),Fh=Wt.join(V_,"views.json"),[Dh,Bh]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),yn=Number(process.env.BOSTRAT_VIEW_MAX)||3,z_="bostrat-view-",jr=t=>`${z_}${t}`,jh=new Set(["bostrat","www","api","views","app"]),La=t=>Object.assign(new Error(t),{status:400}),J_=new Set(["npm","pnpm","yarn","bun"]),Dl="package.json",X_=/[|;&`]|\$\(/,Y_=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Z_=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Uh=t=>String(t).trim().split(/\s+/).filter(Boolean),Q_=/^[A-Za-z_][A-Za-z0-9_]*=/;function tE(t){for(let e of Uh(t))if(!Q_.test(e))return e;return null}function Hh(t){let e=Uh(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function eE(t,e){let n=Hh(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Wt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Wt.resolve(t,n[r+1])}return t}var nE=t=>Zo.stat(t).then(e=>e.isDirectory(),()=>!1),Wh=t=>Zo.stat(t).then(e=>e.isFile(),()=>!1),Fa=t=>Zo.realpath(t).catch(()=>Wt.resolve(t));async function rE(t,e){if(Wt.resolve(t)===Wt.resolve(e))return!0;let[n,r]=await Promise.all([Fa(t),Fa(e)]);return n===r}async function oE(t){let n=(await Zo.readdir(t,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await Wh(Wt.join(t,o.name,Dl))?`./${o.name}`:null))).filter(Boolean)}async function sE(t,e){let[n,r]=await Promise.all([Fa(t),Fa(e)]),o=Wt.relative(n,r);if(o&&(o.startsWith("..")||Wt.isAbsolute(o)))throw La(`serve directory escapes the Track workspace: ${e}`)}async function aE(t,e){if(!await nE(t))throw La(`serve directory does not exist: ${t}`);let n=tE(e);if(!n)return;if(!n.includes("/")&&!await xp(n,{loginShell:!0,failOpen:!0}))throw La(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(X_.test(e)||Y_.has(n)||!J_.has(n)||!Hh(e).includes("run")||Z_.test(e))return;let r=eE(t,e);if(await Wh(Wt.join(r,Dl)))return;let o=await oE(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw La(`no ${Dl} in ${r}${s}`)}var ve=null,Nl=null;async function Fe(){return ve||(Nl||=(async()=>{try{ve=JSON.parse(await Zo.readFile(Fh,"utf8"))}catch{ve={views:[],adoptWatermarks:{}}}finally{Nl=null}return ve.views||=[],ve.adoptWatermarks||={},ve})(),Nl)}async function Te(){await Ft(Fh,ve)}function Le(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var Yo=new Map,Ll=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,qh=12e3;function Fl(t){return Yo.get(t)||null}function Kh(t,{name:e,box:n}={}){let r=Yo.get(t);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:t,name:e||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Yo.set(t,o),o}function Bl(t,e){let n=Yo.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ua(t,{error:e=null}={}){let n=Yo.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Gh(t){let e=[];for(let n of t?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||e.push({box:n.box,from:n.startedAt,to:n.endedAt});return e}function jl(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function iE(t,e){let n=jl(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!jh.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!jh.has(s))return s}}function Qo(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=W_.connect({port:t,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(e,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function Ul(t){if(process.platform!=="darwin"){await Se("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Se("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${t}`]))}catch{return}for(let n of e.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function Vh(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function cE(t,e){let n=new Set(t.views.map(o=>o.port)),r=Vh();if(e){let o=Number(e);if(!Number.isInteger(o)||o<1024||o>65535)throw new Error("invalid port");if(r.has(o))throw new Error(`port ${o} is the node's own API port`);if(n.has(o))throw new Error(`port ${o} is already owned by a view`);return o}for(let o=Dh;o<=Bh;o++)if(!(n.has(o)||r.has(o))&&!await Qo(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Dh}-${Bh}`)}async function lE(t){try{let{stdout:e}=await Se(C,P("list-sessions","-F","#{session_name}"));return e.split(`
114
- `).some(n=>n.trim()===jr(t))}catch{return!1}}async function ts(t){try{await Se(C,P("kill-session","-t",jr(t)))}catch{}}async function Hl(t){if(!await Do())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=jr(t.slug),n=t.serveCwd??t.cwd,r=Wt.dirname(process.execPath);await Se(C,P("new-session","-d","-s",e,"-c",n)),await Se(C,P("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Se(C,P("send-keys","-t",e,"Enter")),await Se(C,P("send-keys","-t",e,"-l",t.cmd)),await Se(C,P("send-keys","-t",e,"Enter"))}async function Lh(t,e=12){try{let{stdout:n}=await Se(C,P("capture-pane","-t",jr(t),"-p"));return String(n||"").trimEnd().split(`
113
+ `)}`:e),this.name="MockRenderError"}},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(`
115
115
  `).filter(Boolean).slice(-e).join(`
116
- `)}catch{return""}}var je=new Map,uE=12e4;async function dE(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await lE(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Lh(t.slug)||"server exited before listening"),je.delete(t.id),await Le(t,t.state,t.error?{error:t.error}:{}),!0}if(await Qo(t.port))return je.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await Le(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>uE?(t.state="error",t.error=await Lh(t.slug)||"never started listening",await Le(t,"error",{error:t.error}),!0):!1;let o=(je.get(t.id)||0)+1;return je.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var Pl=!1;async function Ha(){if(!Pl){Pl=!0;try{let t=await Fe(),e=!1;for(let n of t.views)await dE(n)&&(e=!0);e&&await Te()}finally{Pl=!1}}}async function Wa({trackId:t}={}){await Ha();let e=await Fe();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function zh(t){let n=(await Fe()).views.find(r=>r.id===t);return n?{...n}:null}function Wl(t){return ve&&ve.views.find(e=>e.slug===t)||null}async function ql({trackId:t,name:e,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=Wt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await sE(r,u),await aE(u,n)},d=await Fe();if(i!=="detected"){let h=jl(e),y=String(n).trim(),k=d.views.find(v=>v.trackId===t&&(v.slug===h||jl(v.name)===h||v.cmd===y&&Wt.resolve(v.serveCwd??v.cwd)===u));if(k)return fE(d,k,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(h=>h.state==="live"||h.state==="starting").length>=yn)throw new Error(`view cap reached (${yn} live) \u2014 stop one first`);let m={id:q_().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:iE(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await cE(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Hl(m),d.views.push(m),await Te(),await Le(m,"created",{origin:i}),Bl(t,m.id),i==="detected"&&await Qo(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await Te(),await Le(m,"live")),{...m}}async function fE(t,e,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(e.box=r),o&&(e.branch=o);let i=e.cmd!==n,c=s!==void 0&&!await rE(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Qo(e.port))return await Te(),Bl(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(f=>f.id!==e.id&&(f.state==="live"||f.state==="starting")).length>=yn)throw new Error(`view cap reached (${yn} live) \u2014 stop one first`);return e.origin==="detected"?(await Ul(e.port),e.origin="agent"):await ts(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),je.delete(e.id),await Hl(e),await Te(),await Le(e,"created",{restarted:!0,reused:!0}),Bl(e.trackId,e.id),{...e,reused:!0}}async function Jh(t){let n=(await Fe()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await Ul(n.port):await ts(n.slug),n.state!=="stopped"&&(n.state="stopped",je.delete(n.id),await Te(),await Le(n,"stopped")),{...n}}async function Xh(t){let e=await Fe(),n=e.views.find(o=>o.id===t);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(e.views.filter(o=>o.id!==t&&(o.state==="live"||o.state==="starting")).length>=yn)throw new Error(`view cap reached (${yn} live) \u2014 stop one first`);return await ts(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),je.delete(n.id),await Hl(n),await Te(),await Le(n,"created",{restarted:!0}),{...n}}async function Yh(t){let e=await Fe(),n=e.views.findIndex(o=>o.id===t);if(n===-1)throw new Error("view not found");let r=e.views[n];return r.origin==="detected"?await Ul(r.port):await ts(r.slug),e.views.splice(n,1),je.delete(r.id),await Te(),await Le(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function Zh(t){let e=await Fe(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await ts(r.slug),je.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await Te()),n.length}var pE=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function Qh(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await Fe(),r=n.adoptWatermarks[t.id]??null,o=e.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=e.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[t.id]!==s&&(n.adoptWatermarks[t.id]=s,await Te()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(pE)){let u=Number(l[1]||80);u>=1024&&u<=65535&&a.add(u)}if(!a.size)return[];let i=[];for(let c of a)if(!Vh().has(c)&&!n.views.some(l=>l.port===c)&&await Qo(c))try{i.push(await ql({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}import We from"node:fs/promises";import qe from"node:path";import mE from"node:os";import{randomUUID as hE}from"node:crypto";var Yn=process.env.BOSTRAT_STATE_DIR||qe.join(process.env.HOME||mE.tmpdir(),".bostrat"),ew=qe.join(Yn,"runbooks.json"),qa=qe.join(Yn,"runbooks"),tw=256*1024,wE=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];function es(t){return wE.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Ue=null,Kl=null;async function Ke(){return Ue||(Kl||=(async()=>{let t=!0;try{Ue=JSON.parse(await We.readFile(ew,"utf8"))}catch{Ue={runbooks:[]},t=!1}finally{Kl=null}return Ue.runbooks||=[],!t&&ht()&&await gE().catch(()=>{}),Ue})(),Kl)}async function Zn(){await Ft(ew,Ue)}var ns=t=>Promise.resolve(t).catch(()=>{});function Ka(t){return ns(Pt("runbooks",{...t},{trackId:t.trackId}))}async function gE(){let{records:t,source:e}=await $t("runbooks");if(!(e!=="plane"||!t.length)){await We.mkdir(qa,{recursive:!0});for(let n of t){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await da("runbooks",r.id,o.v,"md");typeof s=="string"&&await We.writeFile(He(r,o.v),s).catch(()=>{})}Ue.runbooks.push(r)}Ue.runbooks.length&&await Zn()}}function Lr(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function He(t,e){return qe.join(qa,`${t.id}.v${e}.md`)}function nw(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function rw(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>tw)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${tw/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=es(t);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return e}function yE(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Ga({trackId:t,status:e}={}){let n=await Ke(),r=t?n.runbooks.filter(o=>o.trackId===t):n.runbooks;return e&&(r=r.filter(o=>o.status===e)),r.map(o=>({...o}))}async function Gl(t){let n=(await Ke()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function ow({trackId:t,trackName:e,name:n,md:r,cwd:o,box:s,agent:a}){if(!t)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=rw(r),c=await Ke(),l={id:hE().slice(0,8),trackId:t,trackName:e||t,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await We.mkdir(qa,{recursive:!0}),await We.writeFile(He(l,1),r),l.versions.push({v:1,mdRel:qe.relative(Yn,He(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Zn(),await Lr(l,"created",{version:1}),Ka(l),ns(cn("runbooks",l.id,1,"md",He(l,1))),{...l}}async function sw(t,{md:e,note:n}={}){let o=(await Ke()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");yE(o,"update");let s=rw(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await We.mkdir(qa,{recursive:!0}),await We.writeFile(He(o,a),e),o.versions.push({v:a,mdRel:qe.relative(Yn,He(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Zn(),await Lr(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Ka(o),ns(cn("runbooks",o.id,a,"md",He(o,a))),{...o}}async function aw(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Ke()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Zn(),await Lr(r,e==="done"?"done":"reopened"),Ka(r),{...r})}async function iw(t){let e=await Ke(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await We.rm(qe.join(Yn,o.mdRel),{force:!0}).catch(()=>{});return await Zn(),r.trackDeletedAt||await Lr(r,"deleted"),ns(oe("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function cw(t){let n=(await Ke()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Lr(r,"orphaned"),r.cwd=null,Ka(r);return n.length&&await Zn(),n.length}async function lw(t){let e=await Ke(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Lr(r,"deleted");for(let o of r.versions||[])await We.rm(qe.join(Yn,o.mdRel),{force:!0}).catch(()=>{});ns(oe("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Zn()),n.length}async function uw(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Ke();for(let r of n.runbooks){await Pt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await cn("runbooks",r.id,o.v,"md",qe.join(Yn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import ss from"node:fs/promises";import Tw from"node:path";import zE from"node:os";import{randomUUID as Aw}from"node:crypto";import bE from"node:fs/promises";import dw from"node:path";import kE from"node:os";var fw=()=>dw.join(process.env.BOSTRAT_STATE_DIR||dw.join(process.env.HOME||kE.tmpdir(),".bostrat"),"integrations.json");async function Ge(){try{return JSON.parse(await bE.readFile(fw(),"utf8"))||{}}catch{return{}}}var Va=["linked","prOpened","prMerged","create"],Vl=["prompt","auto","off"],xE={create:["prompt","off"]},pw=t=>xE[t]??Vl;function za(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of Va){let s=pw(o),a=s.includes(n)?n:"prompt",i=t.ticketFlow?.[o]??e.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function Qn(){let t=await Ge(),e=process.env.BOSTRAT_LINEAR_API_KEY||t.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||t.linear?.defaultTeam||null;return e?{apiKey:e,ticketFlow:za(t),defaultTeam:n}:null}async function Ja(){let t=await Ge(),e=t.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||e.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||e.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||e.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:e.tokenExpiresAt??null,ticketFlow:za(t)}}async function zl(){return za(await Ge())}async function mw(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${Va.join(", ")}`);for(let[r,o]of e){if(!Va.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Va.join(", ")}`);let s=pw(r);if(!s.includes(o))throw new Error(s.length<Vl.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Vl.join(", ")}`)}let n=await Ge();return n.ticketFlow={...za(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Ft(fw(),n,{mode:384}),n.ticketFlow}async function rs(){let t=(await Ge()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function vE(){let t=(await Ge()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var SE={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Xa(){let e=(await Ge()).voiceChat||{},n=e.provider||"openai",r=SE[n];if(!r)return null;let o=n==="openai"?await vE():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function hw(){let t=(await Ge()).cedar||{},e=process.env.BOSTRAT_CEDAR_INGEST_KEY||t.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||t.url||"https://cedar-api.fly.dev";return e?{url:n,ingestKey:e}:null}async function tr(){let t=(await Ge()).slack||{},e=process.env.BOSTRAT_SLACK_BOT_TOKEN||t.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||t.appToken;return!e||!n?null:{botToken:e,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||t.channel||null,maxConcurrent:Number.isInteger(t.maxConcurrent)&&t.maxConcurrent>0?t.maxConcurrent:2}}var Xl={};Ef(Xl,{createComment:()=>ME,findIssueUrl:()=>EE,issueStates:()=>PE,mdToAdf:()=>yw,moveIssue:()=>DE,parseIssueRef:()=>_E,resolveIssue:()=>IE,uploadImage:()=>CE,upsertCard:()=>RE,whoami:()=>Jl});var Fr=t=>String(t.baseUrl||"").replace(/\/$/,""),gw=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function er(t,e,n,r){let o=await fetch(`${Fr(t)}${n}`,{method:e,headers:{authorization:gw(t),accept:"application/json",...r!==void 0?{"content-type":"application/json"}:{}},body:r!==void 0?JSON.stringify(r):void 0}),s=await o.text(),a=null;try{a=s?JSON.parse(s):null}catch{a=null}if(!o.ok){let i=a?.errorMessages?.[0]??Object.values(a?.errors??{})[0]??`HTTP ${o.status}`,c=new Error(`jira: ${i}`);throw c.status=o.status,c}return a}var TE=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,AE=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function _E(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(TE);return r?r[1].toUpperCase():null}let n=e.match(AE);return n?n[1]:null}function EE(t,e=null){let n=null;try{n=e?.baseUrl?new URL(e.baseUrl).host:null}catch{n=null}let r=/https?:\/\/[^\s)]+\/browse\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/g;for(let o of String(t||"").matchAll(r))try{let s=new URL(o[0]).host;if(s===n||/\.atlassian\.net$/i.test(s))return o[0]}catch{}return null}async function IE(t,e){let n;try{n=await er(t,"GET",`/rest/api/3/issue/${encodeURIComponent(e)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${e}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${Fr(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function RE(t,e,{url:n,title:r,subtitle:o}){await er(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var $E=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function OE(t){let e=[],n=0;for(let r of t.matchAll($E)){r.index>n&&e.push({type:"text",text:t.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);e.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?e.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):e.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<t.length&&e.push({type:"text",text:t.slice(n)}),e}function yw(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
117
- `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...OE(o))}),{type:"paragraph",content:r.length?r:[{type:"text",text:" "}]}});return{type:"doc",version:1,content:e.length?e:[{type:"paragraph",content:[{type:"text",text:" "}]}]}}async function ME(t,e,n){let r=await er(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:yw(n)});return r?.id?`${Fr(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function Jl(t){let e=await er(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Fr(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function CE(t,{issue:e,filename:n,contentType:r,buffer:o}){let s=new FormData;s.append("file",new Blob([o],{type:r}),n);let a=await fetch(`${Fr(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:gw(t),"x-atlassian-token":"no-check",accept:"application/json"},body:s}),i=await a.json().catch(()=>null),c=Array.isArray(i)?i[0]:null;if(!a.ok||!c)throw new Error(`jira: attachment upload failed (HTTP ${a.status})`);return c.content??`${Fr(t)}/rest/api/3/attachment/content/${c.id}`}var NE={new:"unstarted",indeterminate:"started",done:"completed"},ww=t=>NE[t?.key]??"started";async function PE(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([er(t,"GET",`/rest/api/3/issue/${n}?fields=status`),er(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:ww(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:ww(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function DE(t,e,n){await er(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var tu={};Ef(tu,{createComment:()=>qE,createIssue:()=>Ql,createProject:()=>Zl,findIssueUrl:()=>UE,issueStates:()=>GE,moveIssue:()=>VE,parseIssueRef:()=>FE,resolveIssue:()=>HE,teams:()=>Ur,uploadImage:()=>KE,upsertCard:()=>WE,whoami:()=>Yl});var BE=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Ae(t,e,n={}){let r=await fetch(BE(),{method:"POST",headers:{"content-type":"application/json",authorization:t.apiKey},body:JSON.stringify({query:e,variables:n})}),o=await r.json().catch(()=>null);if(!r.ok)throw new Error(`linear: HTTP ${r.status}${o?.errors?.[0]?.message?` \u2014 ${o.errors[0].message}`:""}`);if(o?.errors?.length){let s=o.errors[0],a=(s.extensions?.validationErrors??[]).flatMap(i=>Object.values(i?.constraints??{})).join("; ");throw new Error(`linear: ${s.message}${a?` \u2014 ${a}`:""}`)}return o?.data??{}}var jE=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,LE=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function FE(t){let e=String(t||"").trim(),n=e.match(jE);if(n)return n[1].toUpperCase();let r=e.match(LE);return r?r[1]:null}function UE(t){let e=String(t||"").match(/https?:\/\/linear\.app\/[^/\s]+\/issue\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/);return e?e[0]:null}async function HE(t,e){let r=(await Ae(t,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:e})).issue;if(!r)throw new Error(`linear: issue "${e}" not found`);return{issueId:r.id,key:r.identifier,title:r.title,url:r.url,state:r.state?.name??null,team:r.team?.key??null}}async function WE(t,e,{url:n,title:r,subtitle:o}){let s=await Ae(t,"mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }",{input:{issueId:e.issueId,url:n,title:r,subtitle:o}});if(!s.attachmentCreate?.success)throw new Error("linear: attachmentCreate did not succeed");return s.attachmentCreate.attachment?.id??null}async function qE(t,e,n){let r=await Ae(t,"mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } }",{input:{issueId:e.issueId,body:n}});if(!r.commentCreate?.success)throw new Error("linear: commentCreate did not succeed");return r.commentCreate.comment?.url??null}async function Yl(t){let e=await Ae(t,"query { viewer { name email } organization { name urlKey } }");return{user:e.viewer?.name??e.viewer?.email??null,org:e.organization?.name??e.organization?.urlKey??null}}async function KE(t,{filename:e,contentType:n,buffer:r}){let o=await Ae(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
116
+ `)}catch{return""}}var 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
118
  fileUpload(contentType: $contentType, filename: $filename, size: $size) {
119
119
  success
120
120
  uploadFile { uploadUrl assetUrl headers { key value } }
121
121
  }
122
- }`,{contentType:n,filename:e,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function GE(t,e){let n=await Ae(t,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:e.key});if(!n.issue)throw new Error(`linear: issue "${e.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function VE(t,e,n){if(!(await Ae(t,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:e.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Ur(t){return((await Ae(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var bw=255;async function Zl(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>bw,a=s?`${o.slice(0,bw-1).trimEnd()}\u2026`:o,i=await Ae(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function Ql(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Ae(t,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:e,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var _w={linear:{api:tu,config:Qn},jira:{api:Xl,config:Ja}};async function iu(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(_w)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function nr(t){let e=_w[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function cu(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var Ew=()=>process.env.BOSTRAT_STATE_DIR||Tw.join(process.env.HOME||zE.tmpdir(),".bostrat"),ou=()=>Tw.join(Ew(),"tickets.json"),Iw=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Ya=t=>`${Iw()}/track/${t}`,bn=null,eu=null,nu=Promise.resolve();async function is(){return bn||(eu??=ss.readFile(ou(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(bn=t,!e&&ht()){let{records:n,source:r}=await $t("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;bn[s]=i}Object.keys(bn).length&&await su().catch(()=>{})}}return bn}).finally(()=>eu=null),eu)}async function su(){let t=JSON.stringify({tickets:bn},null,2),e=`${ou()}.${process.pid}.tmp`;await ss.mkdir(Ew(),{recursive:!0}),await ss.writeFile(e,t),await ss.rename(e,ou())}async function kn(t=null){if(nu=nu.then(su,su),await nu.catch(()=>{}),!t)return;let e=bn?.[t];e?Pt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):oe("tickets",t).catch(()=>{})}async function _e(t){return(await is())[t]??null}async function Za(){return{...await is()}}async function Rw(){return(await iu()).length>0}async function Hr(t,e,{source:n="api"}={}){let r=await iu();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let f=u.api.parseIssueRef(e);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null,...t.workspace?.kind==="multi"?{repos:(t.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await is())[t.id]=c,await kn(t.id),await $({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await as(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await os(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Mw(t.id,c,"linked"),c}async function lu(t){let e=await is();return e[t]?(delete e[t],await kn(t),!0):!1}var au=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function $w(t,e){if(!t.track?.cwd)return null;let n=ye(await B({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function JE(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${au(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${au(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function XE(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.track.repos?.length&&n.push(t.track.repos.join(" + ")),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(au(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function as(t,e){let n=await nr(e);if(!n)return;let r=await $w(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:Ya(t),title:`Bostrat Track: ${e.track.name}`,subtitle:XE(e,r)})}function Qa(t){let e=new URL(Iw());return`${e.protocol}//${t}.${e.host}/`}var YE={linked:"linked",opened:"prOpened",merged:"prMerged"};function ZE(t,e,n){let r=o=>e.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(t?.type))return r("started")[0]??null}else if(n==="opened"){if(t?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&t?.type!=="completed")return r("completed")[0]??null;return null}async function Ow(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await kn(t),await $({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Mw(t,e,n){let r=await nr(e);if(!r)return;let o=r.cfg.ticketFlow?.[YE[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=cu(s,a),c=ZE(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??e.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,e,c.id),await Ow(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await $({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Aw(),provider:e.provider,key:e.key,url:e.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...e.pr?.number?{prNumber:e.pr.number}:{}}}).catch(()=>{})}async function Cw(t){let e=await _e(t);if(!e)throw new Error("no ticket linked to this Track");let n=await nr(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=cu(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await kn(t)),{key:e.key,url:e.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function uu(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await _e(t);if(!r)throw new Error("no ticket linked to this Track");let o=await nr(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=cu(s,a),c=a.find(u=>u.id===e);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await kn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Ow(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Nw(t,e){let n=await _e(t);if(!n)throw new Error("no ticket linked to this Track");let r=await nr(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await $({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function Pw(t,e,n){if(!e)throw new Error("offerId required");let r=await B({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await uu(t.id,o.payload.to?.id,{mode:"offer"})),await $({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var kw=50;async function Dw(){let t=await Qn();if(!t)throw new Error("no ticketing integration configured");return Ur(t)}function QE(t){let e=t&&typeof t=="object"?t:{},n=(Array.isArray(e.issues)?e.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=e.epic&&String(e.epic.name||"").trim()?{name:String(e.epic.name).trim(),description:e.epic.description?String(e.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>kw)throw new Error(`too many issues (${n.length}); cap is ${kw} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function tI(t,e,n,r){let o=r??await Ur(t),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(e.team){let i=s(e.team);if(i)return i}let a=await _e(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(t.defaultTeam){let i=s(t.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function Bw(t,e){let n=await Qn();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=QE(e),o=await Ur(n),s=await tI(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Aw();return await $({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function jw(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await B({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await $({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await Qn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Ur(a)).find(h=>h.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let m=await Zl(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let h=await Ql(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:m.title,message:h?.message||String(h)})}await $({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await $({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await _e(t.id)&&(p=await Hr(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function os(t,e){let n=await nr(t);n&&await n.api.createComment(n.cfg,t,e)}async function eI(t,e,n){let r=await nr(t);if(!r)return null;let o=await Be(e);if(!o)return null;let s=ae(o,n);if(!s)return null;let a=await ss.readFile(ee(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:t.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var xw=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),ru=new Map;function nI(t,e){if(xw()<=0)return as(t,e);if(ru.has(t))return;let n=setTimeout(()=>{ru.delete(t),_e(t).then(r=>r?as(t,r):null).catch(()=>{})},xw());n.unref?.(),ru.set(t,n)}var vw=new Map;async function Sw(t,e,n){let r=vw.get(t)??0;Date.now()-r<600*1e3||(vw.set(t,Date.now()),await $({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function rI(t,{resolveTrack:e=null}={}){let n=await _e(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await iu())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=e?await e(t.trackId):null;if(!s)return;try{await Hr(s,o,{source:"chat"})}catch(a){await Sw(t.trackId,s.cwd,a)}return}if(!n.track.cwd&&e){let o=await e(t.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await kn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await kn(t.trackId),await as(t.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await kn(t.trackId),await as(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await $w(n,t.trackId).catch(()=>null):null;await os(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
123
-
124
- Agent cost for this Track: ${JE(o)}.`:"")),await Mw(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await eI(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await os(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
125
-
126
- ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await os(n,`**Bostrat** \u2014 design decision: **${r.name}** picked${o?` over ${o}`:""}.`)}break}case"view.state.changed":{if(r.change!=="created"||r.restarted||!r.slug)break;await os(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Qa(r.slug)}
127
- _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await nI(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Sw(t.trackId,s?.cwd??null,o)}}function Lw({resolveTrack:t}){Ht.on("event",e=>{rI(e,{resolveTrack:t}).catch(()=>{})})}async function Fw(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await is(),r=0;for(let[o,s]of Object.entries(n))await Pt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import oI from"node:crypto";import ti from"node:fs/promises";import Uw from"node:path";import sI from"node:os";var Hw=process.env.BOSTRAT_STATE_DIR||Uw.join(process.env.HOME||sI.tmpdir(),".bostrat"),pu=Uw.join(Hw,"pr-links.json"),K=(t,e)=>`${t}#${Number(e)}`,Ww=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=oI.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},xn=null,du=null,fu=Promise.resolve();async function xt(){return xn||(du??=ti.readFile(pu,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(xn=t,!e&&ht()){let{records:n,source:r}=await $t("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;xn[K(i.repo,i.number)]=i}Object.keys(xn).length&&await mu().catch(()=>{})}}return xn}).finally(()=>du=null),du)}async function mu(){let t=JSON.stringify({links:xn},null,2),e=`${pu}.${process.pid}.tmp`;await ti.mkdir(Hw,{recursive:!0}),await ti.writeFile(e,t),await ti.rename(e,pu)}async function It(t){fu=fu.then(mu,mu),await fu.catch(()=>{});let e=xn?.[t];e&&Pt("prlinks",{id:Ww(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var aI=new Set(["api","observed","reconciled"]);async function Jt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await xt(),c=K(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:aI.has(a),firstSeen:Date.now()},await It(K(t,e)),{link:i[c],created:!0})}async function cs(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||n.announced?null:(n.announced=!0,await It(K(t,e)),n)}async function Bt(t,e){return!t||e==null?null:(await xt())[K(t,e)]??null}async function vn(t,e,n){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await It(K(t,e)),r):null}async function qw(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[K(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await It(K(t,e)),o):null}async function Kw(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[K(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await It(K(t,e)),o):null}async function ei(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await It(K(t,e)),r)}async function ni(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await It(K(t,e)),n)}async function ri(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await It(K(t,e)),r)}async function oi(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await It(K(t,e)),n)}async function Gw(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(r.baseFoldAt=n,await It(K(t,e)),r):null}async function Vw(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await It(K(t,e)),n)}async function zw(t,e,n){if(!t||e==null||!n)return null;let r=(await xt())[K(t,e)];return r?(r.lastHeadSha=n,await It(K(t,e)),r):null}async function ls(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await It(K(t,e)),r):null}async function rr(t,e,n=!0){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await It(K(t,e)),r):null}async function us(t,e,n=!0){if(!t||e==null)return null;let r=(await xt())[K(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await It(K(t,e)),r):null}async function Jw(t,e,n){if(!t||e==null)return null;let r=(await xt())[K(t,e)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await It(K(t,e)),r}async function ds(t,e){if(!t||e==null)return null;let n=(await xt())[K(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await It(K(t,e)),n)}var iI=new Set(["merged","closed","lost"]);async function Xw(){return Object.values(await xt()).filter(t=>!iI.has(t.state))}async function Ee(){return Object.values(await xt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function Yw(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await xt(),r=0;for(let[o,s]of Object.entries(n))await Pt("prlinks",{id:Ww(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Qw=t=>Math.round(t*100)/100;function cI(t){return!t||t.kind==="setup"?[]:qt(t.workspace).filter(e=>e&&(e.repo||e.base)).map(e=>({key:e.repo||e.base,slug:e.repo??null,base:e.base??null,name:e.repo?e.repo.split("/").pop():Zw.basename(e.base)}))}function tg(t){let e=new Map;for(let n of t||[])for(let r of cI(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function si(t){let e=t.events?.length?t.events[t.events.length-1].at:0;return Math.max(t.createdAt||0,t.archivedAt||0,t.lastResponseAt||0,e||0)||null}var hu=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function lI(t){if(!Number.isFinite(t)||t<=0)return null;let e=t/864e5;if(e>=1)return`${Math.round(e)}d`;let n=t/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(t/6e4))}m`}function wu(t,e=[]){let n=[],r=t?.events||[],o=new Map,s=0;for(let u of e){if(u.type!=="pr.state.changed")continue;let f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,p]=f.split("#");return`${d.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(hu(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(hu(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${hu(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=ye(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?lI(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function uI(t){let e=[],n=[...t?.boxes||[]].sort((o,s)=>(o.createdAt||0)-(s.createdAt||0));for(let o of n){if(o.role!=="lead"&&o.role!=="handed-off")continue;let s=Array.isArray(o.epochs)&&o.epochs.length?o.epochs.map(a=>a.agent):[o.agent];for(let a of s)a&&e[e.length-1]!==a&&e.push(a)}let r=new Set(n.filter(o=>o.role==="subtask"&&o.agent&&!e.includes(o.agent)).map(o=>o.agent));return{lead:e,subtask:[...r]}}function eg(t,e,n){let r=new Set(t.tracks.map(d=>d.id)),o=0,s=0,a=0,i=null,c=null,l=0;for(let d of t.tracks){d.archivedAt?s++:o++,d.beat?.text&&a++;let p=si(d);p&&(!i||p>i)&&(i=p),d.createdAt&&(!c||d.createdAt<c)&&(c=d.createdAt),l+=e.get(d.id)||0}let u=0,f=0;for(let d of n)r.has(d.trackId)&&(t.slug&&d.repo!==t.slug||t.slug&&(d.state==="merged"?f++:d.state!=="closed"&&u++));return{key:t.key,slug:t.slug,base:t.base,name:t.name,tracks:{total:t.tracks.length,live:o,archived:s,withBeat:a},prs:{open:u,merged:f},usd:Qw(l),firstActivityAt:c,lastActivityAt:i}}async function ng(t){let e=tg(t),n=await Ee().catch(()=>[]),r=new Map,o=new Set;for(let a of e.values())for(let i of a.tracks){if(o.has(i.id))continue;o.add(i.id);let c=await B(i).catch(()=>[]);r.set(i.id,ye(c).totalUsd)}let s=[...e.values()].map(a=>eg(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function rg(t,e){let r=tg(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([Ee().catch(()=>[]),Xn().catch(()=>[]),Wa().catch(()=>[]),Ga().catch(()=>[]),Za().catch(()=>({}))]),l=new Map,u=[],f=(d,p)=>(si(p)||p.createdAt||0)-(si(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let p=await B(d).catch(()=>[]),m=ye(p);l.set(d.id,m.totalUsd);let h=c[d.id]||null;u.push({trackId:d.id,name:d.name,createdAt:d.createdAt??null,archivedAt:d.archivedAt??null,live:!d.archivedAt,beat:d.beat??null,agents:uI(d),handoffs:(d.events||[]).filter(y=>y.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:qt(d.workspace).map(y=>y.repo||Zw.basename(y.base||"")),prs:o.filter(y=>y.trackId===d.id&&(!r.slug||y.repo===r.slug)).map(y=>({repo:y.repo,number:y.number,state:y.state??null,title:y.title??null,url:y.url??null})),mocks:s.filter(y=>y.trackId===d.id).length,views:a.filter(y=>y.trackId===d.id).length,runbooks:i.filter(y=>y.trackId===d.id).map(y=>({name:y.name,status:y.status})),ticket:h?{provider:h.provider,key:h.key,url:h.url??null}:null,usd:Qw(m.totalUsd),lastActivityAt:si(d)})}return{...eg(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as dI}from"node:child_process";import{promisify as fI}from"node:util";import{randomUUID as pI}from"node:crypto";var Wr=fI(dI),ai=t=>new Promise(e=>setTimeout(e,t)),mI=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,og=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,hI=250,wI=400,sg={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}};async function gu(t,e){let n=Date.now()+e;for(;;){if(await Er(t))return!0;if(Date.now()>n)return!1;await ai(hI)}}async function gI(t,e){let n=sg[e]||sg.default;n.escape&&(await At(t,["Escape"],{kind:"swap-exit"}),await ai(150)),n.wipe&&await ya(t,400,{kind:"swap-exit"}),await ba(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Pe(e).submitDelayMs||0;await ai(Math.max(r,250)),await At(t,["Enter"],{kind:"swap-exit"})}async function yI(t,e){try{let{stdout:n}=await Wr(C,P("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ag(t){await Wr(C,P("respawn-pane","-k","-t",t))}async function yu(t,e={}){if(!wt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await tt(t);if(!r)throw new Error(`session "${t}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${t}"`);let o=r.agent||"agent",s=e.agent||ln(n),a=Number(e.exitTimeoutMs)||mI;await q({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Er(t)&&(await gI(t,o),!await gu(t,a)&&(i=!0,await q({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await ag(t),!await gu(t,og))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&Wn(n),l=c?ea():null,u=await yI(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Wr(C,P("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Wr(C,P("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?pa(y,l):y}if(f&&!i&&(await ag(t),!await gu(t,og)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||pI(),m=d?n:Hc(n,p,t),h=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await on(r.cwd),s==="codex"&&await yr(r.cwd),s==="cursor"&&await br(r.cwd),await Kn(t),await Wr(C,P("set-option","-t",t,"@bostrat-sid",p)),await Wr(C,P("set-option","-t",t,"@bostrat-agent",s)),await ai(wI),await Et(t,m,{kind:"swap-launch"}),await q({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:h,forced:i,envRespawned:f}}import CI from"node:fs/promises";var bI=/^Async (?:agent|task) launched/i,kI=t=>{if(typeof t=="string")return t.trim();if(!Array.isArray(t))return"";let e=t.find(n=>n&&n.type==="text"&&typeof n.text=="string");return e?e.text.trim():""};async function ig(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Ut(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
128
- `)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let p=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let p=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(p?Cr(p):null),detail:p?Cr(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&bI.test(kI(d.content))&&s.add(d.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}import fs from"node:fs";import vI from"node:fs/promises";import or from"node:path";import SI from"node:os";import TI from"node:crypto";import AI from"node:zlib";var ii=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),ie=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,qr=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function Sn(t,e){if(!t)return null;let n=String(t);if(e){let o=e.endsWith("/")?e:`${e}/`;if(n===e)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}var bu=t=>`"${String(t).replace(/"/g,'\\"')}"`;function xI(t){let e=typeof t=="string"?t:String(t?.patch??t?.input??""),n=[];for(let r of e.matchAll(/^\*{3} (?:Update|Add|Delete) File: (.+)$/gm))n.push(r[1].trim());return n}function cg(t,e,n){switch(t){case"Shell":{let r=String(e?.command||"").trim();return r?{command:r,description:String(e?.description||"").trim()}:null}case"Read":{let r=e?.path;if(!r)return null;let o=Number(e?.offset)||null,s=o&&Number(e?.limit)?o+Number(e.limit)-1:null;return{command:ie(`read ${Sn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${qr(r)}`:`Read ${qr(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?Sn(e.path,n):null,s=e?.glob?` --glob ${bu(e.glob)}`:"",a=ie(String(r),40);return{command:ie(`grep ${bu(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=e?.glob_pattern;if(!r)return null;let o=e?.target_directory?Sn(e.target_directory,n):null;return{command:ie(`glob ${bu(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${ie(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:ie(`edit ${Sn(e.path,n)}`,140),description:`Edit ${qr(e.path)}`}:null;case"Write":return e?.path?{command:ie(`write ${Sn(e.path,n)}`,140),description:`Write ${qr(e.path)}`}:null;case"Delete":return e?.path?{command:ie(`delete ${Sn(e.path,n)}`,140),description:`Delete ${qr(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>Sn(s,n)).filter(Boolean);return{command:ie(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=xI(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:ie(`apply_patch ${Sn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${qr(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:ie(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var _I=process.env.BOSTRAT_CURSOR_CHATS||or.join(SI.homedir(),".cursor","chats");function dg(t){let e=TI.createHash("md5").update(String(t)).digest("hex");return or.join(_I,e)}function fg(t){try{let e=JSON.parse(fs.readFileSync(or.join(t,"meta.json"),"utf8"));return{createdAtMs:Number(e.createdAtMs)||null,updatedAtMs:Number(e.updatedAtMs)||Number(e.createdAtMs)||null,hasConversation:e.hasConversation===!0}}catch{return null}}function EI(t){let e=dg(t),n;try{n=fs.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=or.join(e,o.name),a=or.join(s,"store.db");if(!fs.existsSync(a))continue;let i=fg(s);if(!i)continue;let c={id:o.name,dir:s,store:a,...i};(!r||c.hasConversation&&!r.hasConversation||c.hasConversation===r.hasConversation&&(c.updatedAtMs||0)>(r.updatedAtMs||0))&&(r=c)}return r}function ci(t,e){if(!t)return null;if(e){let n=or.join(dg(t),e),r=or.join(n,"store.db");if(fs.existsSync(r))return{id:e,dir:n,store:r,...fg(n)||{createdAtMs:null,updatedAtMs:null}}}return EI(t)}function lg(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function II(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=AI.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function RI(t){let e=[],n=0,r=t.length,o=()=>{let s=0,a=0;for(;n<r;){let i=t[n++];if(s+=(i&127)*2**a,!(i&128))break;a+=7}return s};for(;n<r;){let s=o(),a=s>>>3,i=s&7;if(i===2){let c=o(),l=t.subarray(n,n+c);n+=c,a===1&&c===32&&e.push(l.toString("hex"))}else if(i===0)o();else if(i===5)n+=4;else if(i===1)n+=8;else break}return e}function $I(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!ii.has(r.toolName))continue;let o=cg(r.toolName,r.args,e);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}var OI=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
129
- `).trim():"",MI=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],ug=process.getBuiltinModule?.("node:sqlite"),Kr={turns:[],trailingCommands:[],model:null,requestIds:[]};async function ku(t,e){if(!ug)return Kr;let{DatabaseSync:n}=ug,r;try{r=new n(t,{readOnly:!0})}catch{return Kr}try{let o=r.prepare("SELECT value FROM meta WHERE key = '0'").get()||r.prepare("SELECT value FROM meta LIMIT 1").get();if(!o)return Kr;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Kr}let a=s.lastUsedModel||null,i=s.latestRootBlobId?lg(r,s.latestRootBlobId):null;if(!i)return{...Kr,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of RI(i)){let m=II(lg(r,p));if(!m)continue;let h=m.providerOptions?.cursor;if(h?.requestId&&d.push(h.requestId),h?.modelProviderMessageId&&d.push(h.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let v of m.content){let I=v?.providerOptions?.cursor?.modelName;I&&(f=I)}l.push(...$I(m.content,e).map((v,I)=>({...v,id:v.id||`${p}:${I}`}))),u.push(...MI(m.content));let y=OI(m.content);if(!y)continue;let k=[...new Set(u)];u=[],c.push({id:p,text:y,tools:k.length?k:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return Kr}finally{try{r.close()}catch{}}}function pg(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(fs.statSync(t.store).mtimeMs)}catch{n=Date.now()}let r=t.createdAtMs;(!r||r>n)&&(r=n-e*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/e)}}async function mg(t,e,{limit:n=80}={}){let r=ci(t,e);if(!r)return[];let{turns:o,model:s}=await ku(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=pg(r,a.length);return a.map((c,l)=>({role:"assistant",text:c.text,tools:c.tools,ts:i(l),model:c.model||s,sourceId:c.id}))}async function hg(t,e){let n=ci(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await ku(n.store,t);return{chatId:n.id,requestIds:o,models:[...new Set(r.map(a=>a.model).filter(Boolean))],configuredModel:s,updatedAtMs:n.updatedAtMs||null}}async function wg(t,e,{limit:n=80}={}){let r=ci(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await ku(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=pg(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:mn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:mn(u.command,u.description),ts:i+1});return l}async function gg(t,e){let n=ci(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await vI.stat(n.store)).mtimeMs)}catch{return null}}var Gr=class{constructor(e,n=new Set){this.agent=e,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},xu=class extends Gr{transcript(e){return!e?.cwd||!e?.sessionId?null:Ne(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Xc(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?dm(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?ig(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await CI.stat(n)).mtimeMs)}catch{return null}}},vu=class extends Gr{async readMessages(e,n){return mg(e.cwd,e.sessionId,n)}async readCommands(e,n){return wg(e.cwd,e.sessionId,n)}async updatedAt(e){return gg(e.cwd,e.sessionId)}},Su=class extends Gr{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return hm(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return wm(e.cwd,this.sessionId(e),n)}async updatedAt(e){return gm(e.cwd,this.sessionId(e))}},NI=new Gr("unknown"),yg=new Map;function Tu(t){return yg.set(t.agent,t),t}function ps(t){return yg.get(t)||NI}Tu(new xu("claude",dl));Tu(new vu("cursor",ii));Tu(new Su("codex",Ko));var Au=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function _u(t,e={}){return(await ps(t.agent).readMessages(t,e)).map(Au(t))}async function bg(t,e={}){return(await ps(t.agent).readCommands(t,e)).map(Au(t))}async function kg(t,e={}){return(await ps(t.agent).readJobs(t,e)).map(Au(t))}var Sg=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Ru=/Chat about this/i,sr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,xg=/^(type something|chat about this|submit)\b/i,ar=/^[─—-]{6,}\s*$/,$u=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function ir(t){let e=t.search($u);return(e>=0?t.slice(0,e):t).trim()}function PI(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function DI(t){let e=String(t).trim();return!!(!e||ar.test(t)||$u.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function BI(t,e=""){let n=[],r=/([☐☒])\s+([^☐☒←→✔]+)/g,o;for(;(o=r.exec(t))!==null;)n.push({label:o[2].trim(),answered:o[1]==="\u2612",current:!1});if(!n.length)return null;let s=jI(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function jI(t,e){let n=String(e).toLowerCase(),r=t.findIndex(c=>!c.answered),o=r>=0?r:t.length-1;if(!n)return o;let s=t.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(d=>d.length>3&&n.includes(d)).length;if(u)return u;let f=l.slice(0,8).trim();return f.length>=4&&n.includes(f)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function vg(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Tg=/^\s*Switch model\?\s*$/m;function LI(t,e=""){let n=String(t);if(!Tg.test(n))return null;let r=n.replace(/\r/g,"").split(`
130
- `),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let f=r[u].match(sr);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!ar.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:ir(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}var Ag=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,FI=/^\s*[─—▔-]{6,}\s*$/;function UI(t,e=""){let n=String(t).replace(/\r/g,"").split(`
131
- `),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Ag.test(n[r]))return null;let o=r-1;for(;o>=0&&n[o].trim()==="";)o--;let s=[];for(;o>=0;o--){let c=n[o].match(sr);if(!c)break;s.unshift({cursor:!!c[1],n:Number(c[2]),label:c[4].trim()})}if(s.length<2||s.some((c,l)=>c.n!==l+1))return null;let a=[];for(let c=o;c>=0&&a.length<6&&!(FI.test(n[c])||ar.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:ir(c.label),description:""}));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(c=>c.cursor)),promptId:`claude:confirm-gate:${i.map(c=>c.label).join("|")}`.slice(0,240),questions:[{question:a.join(" ")||"Claude Code is asking you to confirm",header:"Confirm",multiSelect:!1,options:i}]}}function HI(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
132
- `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(sr);l&&o.push({cursor:!!l[1],label:l[4].trim()})}let s=o.filter(c=>/^(submit answers|cancel)$/i.test(c.label));if(s.length<2)return null;let a=s.map(c=>({label:c.label,description:""})),i=Math.max(0,s.findIndex(c=>c.cursor));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}var Eu=/Resume from summary/i,WI=/^\s*This session is\b/;function qI(t,e=""){let n=String(t);if(!Eu.test(n))return null;let r=n.replace(/\r/g,"").split(`
133
- `),o=-1;for(let u=r.length-1;u>=0;u--)if(WI.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let f=r[u].match(sr);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!ar.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>Eu.test(u.label)))return null;let i=s.map(u=>({label:ir(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}var cr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function Ou(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(sr);o&&e.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=e.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}var _g=/Choose the text style that looks best with your terminal/,Eg=/Let's get started\./;function KI(t,e=""){let n=String(t);if(!_g.test(n)||!Eg.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
134
- `),o=Ou(r);return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:ir(s.label),description:""}))}]}:null}var Ig=/Select login method:/;function GI(t,e=""){let n=String(t);if(!Ig.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
135
- `),o=Ou(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:ir(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}var Mu=/Paste code here if prompted/,VI=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function zI(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!VI.test(o)||Mu.test(t[r]))break;n+=o}return n}function JI(t,e=""){let n=String(t);if(!Mu.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
136
- `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=zI(r,s);break}return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,answerMode:"text",setup:"oauth",url:o,inputLabel:"Paste your sign-in code",promptId:"claude:setup:oauth",questions:[{question:"Open the sign-in link, approve access, then paste the code you're shown back here. The code goes straight to the Claude CLI on your box \u2014 bostrat relays the keystrokes and never stores it.",header:"Log in",multiSelect:!1,options:[]}]}:null}var Iu=/OAuth error:/,XI=/Press Enter to retry\./;function YI(t,e=""){let n=String(t);if(!Iu.test(n)||!XI.test(n)||cr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
137
- `)].reverse().find(a=>Iu.test(a))||"OAuth error").trim();return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}var Rg=/Quick safety check: Is this a project you created or one you trust/;function ZI(t,e=""){let n=String(t);if(!Rg.test(n)||cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
138
- `),o=Ou(r);return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:ir(s.label),description:""}))}]}:null}var $g=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function QI(t,e=""){let n=String(t);if(cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
139
- `),o=-1;for(let l=r.length-1;l>=0;l--)if(r[l].trim()!==""){o=l;break}if(o<0)return null;let s=r[o].match($g);if(!s)return null;let a=[];for(let l=o-1;l>=0&&a.length<3;l--){let u=r[l].trim();if(!u){if(a.length)break;continue}if(ar.test(r[l])||$u.test(u))break;a.unshift(u)}let i=s[1].trim();i&&a.push(i);let c=a.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${c}`.slice(0,200),questions:[{question:c,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function tR(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Ru.test(t[r])){e=r;break}if(e<0)return null;let n=-1;for(let r=e-1;r>=0;r--)if(/[☐☒]/.test(t[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:e,optStart:n+1,optEnd:e}}function eR(t,e=""){let n=String(t);if(!Sg.test(n)||!Ru.test(n))return null;let r=n.replace(/\r/g,"").split(`
140
- `),o=tR(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let S=i;S<c;S++){let N=r[S].match(sr);N&&(u<0&&(u=S),l.push({line:S,cursor:!!N[1],checked:N[3]!=null&&N[3]!==" ",hasCheckbox:N[3]!=null,label:N[4].trim(),desc:""}))}if(!l.length)return null;for(let S of l)for(let N=S.line+1;N<Math.min(r.length,S.line+4)&&!(sr.test(r[N])||ar.test(r[N]));N++){let D=r[N].trim();if(!(!D||DI(r[N])||xg.test(D))){S.desc=PI(D);break}}let f=l.filter(S=>!xg.test(S.label));if(!f.length)return null;let d=f.some(S=>S.hasCheckbox),p=Math.max(0,f.findIndex(S=>S.cursor)),m=s>=0?s+1:0,h=[];for(let S=m;S<u;S++){let N=r[S].trim();N&&!ar.test(r[S])&&!/[☐☒]/.test(r[S])&&h.push(N)}let y=h.join(" ")||(s>=0?vg(r[s]):"")||"The agent is asking a question",k=s>=0?BI(r[s],y):null,v=k?.currentTabLabel||(s>=0?vg(r[s]):""),I=f.map(S=>({label:ir(S.label),description:S.desc})),R=I.map(S=>S.label).join("|"),O=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${v}:${y}:${R}`.slice(0,240),M=k?`claude:ask:${k.tabs.map(S=>S.label).join("|")}`.slice(0,240):O;return{provider:"claude",box:e,multiSelect:d,cursorIndex:p,promptId:O,groupId:M,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:y,header:v,multiSelect:d,options:I}]}}function Vr(t,e){let n=String(e||"");return n?t==="claude"?Sg.test(n)&&Ru.test(n)||Tg.test(n)||Eu.test(n)||Ag.test(n.trimEnd().split(`
141
- `).pop()||"")?!0:cr.test(n)?!1:_g.test(n)&&Eg.test(n)||Ig.test(n)||Mu.test(n)||Iu.test(n)||Rg.test(n)||$g.test(n.trimEnd().split(`
142
- `).pop()||""):t==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate/i.test(n)?!0:t==="cursor"||t==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function lr(t,e){return!t||!e?null:t.agent==="claude"?HI(e,t.name)||eR(e,t.name)||LI(e,t.name)||qI(e,t.name)||YI(e,t.name)||JI(e,t.name)||GI(e,t.name)||KI(e,t.name)||ZI(e,t.name)||QI(e,t.name)||UI(e,t.name):t.agent==="cursor"||t.agent==="codex"?Kp(t.agent,e,t.name):null}async function Cu(t){if(!t)return null;let e=await dn(t.name).catch(()=>"");return lr(t,e)}function Og(t,e){let n=Array.isArray(e?.[0])?e[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(t?.cursorIndex)?t.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(t?.multiSelect){let a=r;for(let i of[...n].sort((c,l)=>c-l))s.push(...o(a,i),"Enter"),a=i;s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function Mg(t,e){let n=Number.isInteger(t?.questionIndex)?t.questionIndex:0,o=(Number.isInteger(e)?e:n)-n;if(!o)return[];let s=o>0?"Tab":"BTab";return Array.from({length:Math.abs(o)},()=>s)}function zr(t){return typeof t=="string"?t.trim():""}import{execFile as nR}from"node:child_process";import{promisify as rR}from"node:util";var Pu=rR(nR),Cg=64*1024*1024;async function yt(t,e,{input:n}={}){let r=Pu("git",["-C",t,...e],{maxBuffer:Cg});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function li(t){if(!t)return!1;try{let{stdout:e}=await yt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Ng(t){let{stdout:e}=await yt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function oR(t){try{return await yt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function ms(t,e="bostrat: subtask snapshot"){let{stdout:n}=await yt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Pu("git",["-C",t,...a],{maxBuffer:Cg,env:o,...i});try{let a=await oR(t);a&&await s(["read-tree","HEAD"]),await s(["add","-A"]);let{stdout:i}=await s(["write-tree"]),c=i.trim(),l=a?["commit-tree",c,"-p","HEAD","-m",e]:["commit-tree",c,"-m",e],{stdout:u}=await s(l);return u.trim()}finally{try{await Pu("rm",["-f",r])}catch{}}}async function Du(t,e){let{stdout:n}=await yt(t,["status","--porcelain"]);return n.trim()?(await yt(t,["add","-A"]),await yt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function Bu(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await yt(t,["status","--porcelain"]),r=n.split(`
143
- `).filter(Boolean);return e&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function ju(t,e){if(!e)return 0;try{let{stdout:n}=await yt(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Jr(t,e,n,r){return await yt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Pg(t,e){try{return await yt(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function Dg(t,e,n){return await yt(t,["worktree","add",e,n]),e}async function ui(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await yt(t,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(e.trim())return e.trim()}catch{}for(let[e,n]of[["refs/remotes/origin/main","origin/main"],["refs/remotes/origin/master","origin/master"],["refs/heads/main","main"],["refs/heads/master","master"]])try{return await yt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function Bg(t){await yt(t,["init","-q"]),await yt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function ne(t,e){try{await yt(t,["worktree","remove","--force",e])}catch{try{await yt(t,["worktree","prune"])}catch{}}}function sR(t){let e=/(\d+) files? changed/.exec(t),n=/(\d+) insertions?\(\+\)/.exec(t),r=/(\d+) deletions?\(-\)/.exec(t);return{files:e?Number(e[1]):0,insertions:n?Number(n[1]):0,deletions:r?Number(r[1]):0}}function Lu(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Fu(t,e,n,r){let{stdout:o}=await yt(t,Lu(["diff","--shortstat",e,n],r));return sR(o)}async function Uu(t,e,n,r){let{stdout:o}=await yt(t,Lu(["diff","--name-only",e,n],r));return o.split(`
144
- `).map(s=>s.trim()).filter(Boolean)}async function Hu(t,e,n,r){let{stdout:o}=await yt(t,Lu(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Nu(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Nu(t,o,["--3way","--whitespace=nowarn"]),a=await aR(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Nu(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Nu(t,e,n){try{return await yt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function aR(t){try{let{stdout:e}=await yt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
145
- `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import Wu from"node:fs/promises";import Lg from"node:path";import iR from"node:os";var Fg=process.env.BOSTRAT_STATE_DIR||Lg.join(process.env.HOME||iR.tmpdir(),".bostrat"),Ug=()=>Lg.join(Fg,"usage-caps.json"),Hg=.8;function Wg(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(e.getDate()-(e.getDay()+6)%7),e.getTime()}function qg(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(1),e.getTime()}function Kg(t){let e=new Date(Wg(t));return e.setDate(e.getDate()+7),e.getTime()}function Gg(t){let e=new Date(qg(t));return e.setMonth(e.getMonth()+1),e.getTime()}var Xr={weekly:{startOf:Wg,nextStart:Kg,capKey:"weeklyUsd"},monthly:{startOf:qg,nextStart:Gg,capKey:"monthlyUsd"}},ce=null,Ku=!1,Ve={weekly:0,monthly:0},ur=new Map,Yr=new Set,qu=null,cR=t=>{let e=ur.get(t);return e||(e={weekly:0,monthly:0},ur.set(t,e)),e};function fi(t=Date.now()){for(let[e,n]of Object.entries(Xr)){let r=n.startOf(t);if(r!==Ve[e]){Ve[e]=r;for(let o of ur.values())o[e]=0;for(let o of Yr)o.includes(`:${e}:`)&&!o.endsWith(`:${r}`)&&Yr.delete(o)}}}function Vg(t,e){let n=ce?.caps?.[t]?.[Xr[e].capKey];return Number.isFinite(n)&&n>0?n:null}function Zr(t,e=Date.now()){if(!t||!Ku||!ce?.caps)return null;fi(e);let n=ur.get(t);for(let r of Object.keys(Xr)){let o=Vg(t,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:t,window:r,limitUsd:o,spentUsd:di(s),resetsAt:Xr[r].nextStart(e)}}return null}function zg(t,e){let n=`hit:${t}:${e.provider}:${e.window}:${Ve[e.window]}`;return Yr.has(n)?!1:(Yr.add(n),!0)}function jg(t,e,n,r=Date.now()){if(!t||!e)return;fi(r);let o=cR(t);for(let[s,a]of Object.entries(Xr))n>=Ve[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function lR(t,e,n=Date.now()){if(!ce?.caps||!qu)return;let r=ur.get(t);if(r)for(let[o,s]of Object.entries(Xr)){let a=Vg(t,o);if(a==null)continue;let i=r[o]||0;if(i<a*Hg||i>=a)continue;let c=`warn:${t}:${o}:${Ve[o]}`;if(!Yr.has(c)){Yr.add(c);try{let l=await qu(e);if(!l)continue;await $(l,{type:"usage.cap.warning",actor:"system",provider:t,payload:{provider:t,window:o,limitUsd:a,spentUsd:di(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function Jg(t){let e={};for(let[n,r]of Object.entries(t||{})){if(typeof n!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n))throw new Error(`bad provider key "${n}"`);let o={};for(let s of["weeklyUsd","monthlyUsd"]){let a=r?.[s];if(a==null||a==="")continue;let i=Number(a);if(!Number.isFinite(i)||i<=0)throw new Error(`${n}.${s} must be a positive number or null`);o[s]=i}Object.keys(o).length&&(e[n]=o)}return e}async function pi(t,{source:e="local",updatedAt:n=Date.now()}={}){ce={caps:Jg(t),updatedAt:n,source:e};try{await Wu.mkdir(Fg,{recursive:!0}),await Wu.writeFile(Ug(),JSON.stringify(ce,null,2))}catch{}return hs()}function hs(t=Date.now()){fi(t);let e=new Set([...ur.keys(),...Object.keys(ce?.caps||{})]),n={};for(let r of e){let o=ur.get(r);n[r]={weeklyUsd:di(o?.weekly||0),monthlyUsd:di(o?.monthly||0)}}return{caps:ce?.caps||{},configured:!!ce,seeded:Ku,updatedAt:ce?.updatedAt??null,spent:n,windows:{weekly:{start:Ve.weekly,resetsAt:Kg(t)},monthly:{start:Ve.monthly,resetsAt:Gg(t)}},warnPct:Hg}}async function Xg({listTracks:t,getTrack:e}={}){qu=e||null,fi();try{let o=JSON.parse(await Wu.readFile(Ug(),"utf8"));ce={caps:Jg(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{ce=null}let n=Date.now();Ht.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=De(o);jg(o.provider||"unknown",s,o.ts??Date.now()),lR(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(Ve.weekly,Ve.monthly);for(let o of await t?.()||[]){let s;try{s=await B(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||jg(a.provider||"unknown",De(a).usd,i,n)}}return Ku=!0,hs()}var di=t=>Math.round(t*1e4)/1e4;import uR from"node:fs/promises";import dR from"node:os";import Yg from"node:path";var mi=t=>new Promise(e=>setTimeout(e,t)),fR=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,pR=300,ey=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 5"},{alias:"sonnet",label:"Sonnet 5"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],mR=new Set([...ey.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),hR=[{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.6-terra",label:"GPT-5.6 Terra"},{alias:"gpt-5.6-luna",label:"GPT-5.6 Luna"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}],ny=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),ry=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}];function oy(t){return t==="codex"?hR:t==="cursor"?ry:ey}function Zg(t){let e=String(t||"").split(`
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
146
  `),n=e.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(e[o]))break}return r.length?r:null}function Qg(t){let e=String(t||"").split(`
147
- `),n=e.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(e[o]))break}return r.length?r:null}function wR(t){let e=String(t||"").split(`
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
148
  `);for(let n=e.length-1;n>=0;n--){let r=e[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function ty(t){let e=String(t||"").split(`
149
- `),n=e.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(e[n]),o=[];for(let s=n+1;s<e.length;s++){let a=e[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function sy(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim();if(t==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!ny.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=ry.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!mR.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ws(t,e,{timeoutMs:n=fR}={}){let r=Date.now()+n;for(;;){let o=await mt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await mi(pR)}}async function gR(t,e){return await Et(t,`/model ${e.model}`,Pe("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function yR(t,e){await ga(t),await Et(t,"/model",{enter:!1}),await mi(250),await At(t,["Enter"],{kind:"model-switch"});let n=await ws(t,Zg);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await At(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${e.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=wR(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await Et(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await ws(t,i=>Qg(i)||!Zg(i)&&o(i)&&{done:o(i)});if(!s)throw await At(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?ny.get(e.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await At(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await Et(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await ws(t,i=>!Qg(i)&&o(i));if(!a)throw new Error("codex never confirmed the model change");return{verified:!0,model:a.model,effort:a.effort}}async function bR(){let t=Yg.join(process.env.CURSOR_CONFIG_DIR||Yg.join(dR.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await uR.readFile(t,"utf8"))?.model||null}catch{return null}}async function kR(t,e){await ga(t),await ba(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await ws(t,ty),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(e.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await ws(t,ty,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await At(t,["Down"],{kind:"model-switch"}),await mi(250)}if(!n||!s||!o(s)){await At(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await At(t,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${e.filter}"`:"cursor model picker didn't open")}await At(t,["Enter"],{kind:"model-switch"}),await mi(800);let a=await bR();return{verified:!!a&&(a.modelId===e.model||a.displayModelId===e.model||r(a.displayName).startsWith(r(e.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function ay(t,e,n){return t==="codex"?yR(e,n):t==="cursor"?kR(e,n):gR(e,n)}var Xt=t=>t||null;function wi(t){let e=t||{},n=xR(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function xR(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{text:t.text,boxRole:t.boxRole,...t.kind?{kind:t.kind}:{},...t.attachments?.length?{attachments:t.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{saidTs:t.saidTs,text:t.text,boxRole:t.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{text:t.prompt,kind:t.kind,boxRole:t.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Xt(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered,checkpoint:t.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Xt(t.toAgent),payload:{box:t.box,fromAgent:t.fromAgent,toAgent:t.toAgent,reason:t.reason,error:t.error,rolledBack:t.rolledBack,handoffStartedAt:t.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Xt(t.toAgent),payload:{task:t.task,subtaskId:t.subtaskId,reason:t.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:t.box||t.from,provider:Xt(t.agent),payload:{...Qr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Xt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Qr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{change:"subtask-resumed",...Qr(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...Qr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Qr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Qr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var hi=Promise.resolve(),Vu=t=>(hi=hi.then(t).catch(()=>{}),hi),cy=()=>hi;function ly(t,e){let n=wi(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Vu(()=>$(r,n))}var iy=new Set;async function uy(t){if(!(!t?.id||iy.has(t.id))){iy.add(t.id);try{let e=await B(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=wi(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(t,o)}}catch{}}}function Qr(t){let{type:e,at:n,...r}=t;return r}var Gu=new Map;function dy(t,e,n){!n?.promptId||Gu.get(t.id)===n.promptId||(Gu.set(t.id,n.promptId),Vu(()=>$(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Xt(n.provider||e?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function zu(t,e,n,r={}){Gu.delete(t.id),Vu(()=>$(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Xt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as c$}from"node:crypto";var py=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,my=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,hy=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,wy=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|clean|interactive)\s)rebase(?!s\b)(?:\s+(?:it|this|that|them|(?:this|that|the)\s+branch))?(?:\s+(?:on(?:to)?|against|atop|over)\s+\S+)?/.source,vR=new RegExp(`\\b(?:${py}|${my}|${hy}|${wy})\\b`,"i"),SR=/\b(?:want\s+me\s+to|would\s+you\s+like(?:\s+me)?(?:\s+to)?|should\s+i|shall\s+i|do\s+you\s+want(?:\s+me)?(?:\s+to)?|happy\s+to|ready\s+for\s+me\s+to|let\s+me\s+know\s+(?:if|when|whether|and)|say\s+the\s+word|give\s+me\s+the\s+(?:word|go[\s-]?ahead)|if\s+you(?:'d|\s+would)?\s+(?:like|want|prefer)|just\s+say\s+(?:so|yes|go))\b/i,TR=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,AR=90;function _R(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function gy(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of _R(n).reverse()){if(!vR.test(r))continue;let o=r.endsWith("?")&&r.length<=AR&&TR.test(r);if(SR.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var yy=["commit","rebase","push","pr"],ER=new RegExp(`\\b(?:${py})\\b`,"i"),IR=new RegExp(`\\b(?:${wy})\\b`,"i"),RR=new RegExp(`\\b(?:${my})\\b`,"i"),$R=new RegExp(`\\b(?:${hy})\\b`,"i"),OR=/\b(?:to|into|onto|on|updat(?:e|es|ing))\s+(?:the|that|this)\s+(?:open\s+|existing\s+|current\s+|same\s+)?(?:draft\s+)?(?:pr|pull\s+request)\b/i;function by(t){let e=String(t||""),n=[];ER.test(e)&&n.push("commit"),IR.test(e)&&n.push("rebase"),RR.test(e)&&n.push("push");let r=OR.test(e);!r&&$R.test(e)&&n.push("pr");let o=null;if(r){let s=e.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||e.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function gi(t,{branch:e=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(t),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=e?` on this branch (${e})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${e?` (${e})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${e?` (${e})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}var MR=3e4;function ky(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&e-r.lastActivity<=MR&&n.push({box:r.box,from:r.from,to:e+1e12}),n}var CR=2500;function xy(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to+CR||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}var fy={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function vy(t){let e=(Array.isArray(t)?t:[]).filter(r=>fy[r]).map(r=>fy[r]);return`The ship you were given the go-ahead for${e.length?` (${e.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}import{execFile as jR}from"node:child_process";import{promisify as LR}from"node:util";import NR from"node:fs";import Ju from"node:fs/promises";import _y from"node:path";import PR from"node:os";var Ey=process.env.BOSTRAT_STATE_DIR||_y.join(process.env.HOME||PR.tmpdir(),".bostrat"),yi=_y.join(Ey,"github-metrics.json"),Iy=60*6e4,Sy=5*6e4,DR=6e4,to=Date.now(),ze=new Map,Tn=null,dr=0,Ty=0,bi=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(NR.readFileSync(yi,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Iy?(to=t.windowStart,ze=new Map(Object.entries(t.byCaller||{})),Tn=t.lastHour||null):bi(new Map(Object.entries(t.byCaller||{})))&&(Tn={start:t.windowStart,total:bi(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var Ay=Promise.resolve();function Ry(t,e=!1){if(!e&&t-Ty<DR)return;Ty=t;let n=JSON.stringify({windowStart:to,byCaller:Object.fromEntries(ze),lastHour:Tn});Ay=Ay.then(()=>Ju.mkdir(Ey,{recursive:!0})).then(()=>Ju.writeFile(`${yi}.tmp`,n)).then(()=>Ju.rename(`${yi}.tmp`,yi)).catch(()=>{})}function $y(t){t-to<Iy||(Tn={start:to,total:bi(ze),byCaller:Object.fromEntries(ze)},Tn.total&&console.log(`[github] ${Tn.total} calls last hour`,Tn.byCaller),to=t,ze=new Map,Ry(t,!0))}function Je(t="other",e=Date.now()){$y(e),ze.set(t,(ze.get(t)||0)+1),Ry(e)}function Oy(t=Date.now()){return $y(t),{windowStart:to,total:bi(ze),byCaller:Object.fromEntries(ze),lastHour:Tn,backoffUntil:dr>t?dr:null}}var BR=/rate limit|too quickly|abuse detection|HTTP 429/i;function An(t){return BR.test(String(t?.message||t||""))}function _n(t,e=Date.now()){return dr<=e&&console.warn(`[github] rate limited \u2014 backing off ${Sy/6e4} min:`,String(t?.message||t||"").slice(0,200)),dr=Math.max(dr,e+Sy),dr}function ki(t=Date.now()){return Math.max(0,dr-t)}var My=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,gs=new Map;function xi(t,e,n){let{state:r,reason:o}=el(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of gs)a-c.at>=My()&&gs.delete(i);gs.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=gs.get(t);return s?Date.now()-s.at>=My()?(gs.delete(t),!1):s.busy:!1}async function Xu(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await mt(t,{lines:_t}).catch(()=>"");if(!xi(t,e,r))return Jp(e,r)?{working:!0,prompted:!1}:n;if(Vr(e,r)){let o=await dn(t).catch(()=>r);if(lr({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function vi(t,e){return(await Xu(t,e)).working}var Cy=LR(jR),Ny=[4e3,15e3,45e3],FR=2500,UR=5*6e4;async function Yu(t,e,{midTurn:n=vi,pollMs:r=FR,capMs:o=UR}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}var HR=15e3,WR=9e4,qR=2e3,KR=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,GR=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,VR=/\s--web\b/,Py=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,zR=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,JR=/^track\/.+-([0-9a-f]{8})$/;function XR(t,e){let n=String(t||"").match(JR);return!!n&&n[1]!==e}function Zu(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Dy(t){let e=String(t||""),n=e.match(KR);if(!n)return null;let r=Zu(e,n.index);return VR.test(r)?null:{repo:r.match(Py)?.[1]??null,head:r.match(zR)?.[1]??null}}var YR=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function By(t){let e=String(t||""),n=e.match(GR);if(!n)return null;let r=Zu(e,n.index),o=Zu(e,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){YR.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Py)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var ZR="number,url,title,headRefName,createdAt";async function QR(t,e,{exclude:n=()=>!1}={}){let r=async i=>{Je("observer");let{stdout:c}=await Cy("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",ZR],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw An(l)&&_n(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=e-HR&&!n(c)),s=await me({slug:t}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function t$(t,e){if(e)return[e];let n=[];for(let r of jy(t)){let o=await et(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Oe().catch(()=>[])){let o=await et(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function jy(t){let e=t.workspace;return e?.kind==="multi"?(e.repos??[]).map(n=>n?.base).filter(Boolean):[e?.base||t.cwd].filter(Boolean)}async function e$(t,e,n){let r=t.workspace?.branch,o=e?.head??null,s=c=>r&&c===r||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||XR(c.headRefName,t.id),i=[];for(let c of await t$(t,e?.repo??null)){let l;try{l=await QR(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await Bt(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===t.id&&f.announced)return{settled:!0};if(f&&f.trackId!==t.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==t.id||f.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}var En=new Set;function Ly(t,e,{delays:n=Ny,turnEnd:r}={}){let o=Dy(e.command);if(!o)return null;let s=!!By(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(En.has(a))return null;En.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>setTimeout(h,l));let u=await e$(t,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await Jt({repo:f,number:d.number,trackId:t.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await cs(f,d.number))?null:(await Yu(e.box,e.agent,r),await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{En.delete(a)}})();return c.catch(()=>En.delete(a)),c}var n$="number,url,title,headRefName,state";function Fy(t,e,{delays:n=Ny,turnEnd:r,openedWait:o}={}){let s=By(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(En.has(a))return null;En.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let m=jy(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of m){let y=await et(h).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=t.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,h)=>{let y=await Bt(c,m.number);return y&&y.trackId!==t.id?null:(y||await Jt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await ls(c,m.number),h&&((await B(t).catch(()=>[])).some(I=>I.type==="pr.state.changed"&&(I.payload?.change==="auto-armed"||I.payload?.change==="merged")&&I.payload?.number===m.number)||(await Yu(e.box,e.agent,r),await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(O=>setTimeout(O,m));let h=[];for(let O of f)try{Je("observer");let{stdout:M}=await Cy("gh",["pr","view",String(l),"--repo",O,"--json",n$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await me({slug:O})}}),S=JSON.parse(M||"null");S?.number&&h.push({slug:O,pr:S})}catch(M){An(M)&&_n(M)}let y=h.find(O=>O.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!y)continue;c=y.slug;let k=y.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return d(k,!0);continue}let v=await Bt(c,k.number);if(v&&v.trackId!==t.id)return null;v||await Jt({repo:c,number:k.number,trackId:t.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Yu(e.box,e.agent,r);let I=await B(t).catch(()=>[]);if(I.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="merged"&&O.payload?.number===k.number))return null;let R=O=>O.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="opened"&&M.payload?.number===k.number),z=0;if(!R(I)){if(Dy(e.command)&&await cs(c,k.number)){let O=Date.now();z=O+1,await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:O,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await Bt(c,k.number))?.announced){let O=o?.capMs??WR,M=o?.pollMs??qR,S=Date.now()+O;for(;Date.now()<S&&(await new Promise(N=>setTimeout(N,M)),!R(await B(t).catch(()=>[]))););}}return await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),z),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await Rn(c,k.number).catch(()=>{}),await In(c,k.number).catch(()=>{}),await vn(c,k.number,"merged"),k}return p?d(p,!1):null}finally{En.delete(a)}})();return i.catch(()=>En.delete(a)),i}import r$ from"node:fs/promises";import td from"node:path";import o$ from"node:os";var s$=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/;function Qu(t,e){let n=new RegExp(`\\b${e}="([^"]*)"`).exec(t);return n?n[1]:null}function Si(t){let e=s$.exec(String(t||""));if(!e)return null;let[,n,r]=e,o=Qu(n,"from"),s=o?o.replace(/^uds:/,""):null,a=Uy(s);return{socket:s,pid:a,name:Qu(n,"from-name"),mode:Qu(n,"from-mode"),text:r.trim()}}function Uy(t){let e=/(\d+)\.sock$/.exec(String(t||""));return e?Number(e[1]):null}function a$(){return process.env.CLAUDE_CONFIG_DIR?td.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):td.join(o$.homedir(),".claude","sessions")}async function i$(t){let e=Uy(t);if(!e)return null;try{let n=await r$.readFile(td.join(a$(),`${e}.json`),"utf8"),r=JSON.parse(n);return{pid:e,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function Hy(t,e){let n=await i$(t?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof e=="function")try{o=await e(n?.sessionId??null,r)??null}catch{o=null}return{socket:t?.socket??null,pid:n?.pid??t?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??t?.name??null,mode:t?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}function ys(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Wy(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function qy(t){return`job|${t.box}|id:${t.id}`}var ed=new Map,Ti=new Map;async function nd(t){return ed.has(t.id)?ed.get(t.id):(Ti.has(t.id)||Ti.set(t.id,B(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(ys({role:o,sourceId:r.payload.sourceId})),n.keys.add(ys({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(ys({role:"user",sourceId:r.payload.sourceId})),n.keys.add(ys({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(Wy({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(qy({id:r.payload.id,box:r.boxId}));return ed.set(t.id,n),n}).finally(()=>Ti.delete(t.id))),Ti.get(t.id))}async function Ky(t,e){try{let n=(await nd(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=ys(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?Si(r.text):null;if(s){await $(t,{type:"peer.message",actor:"peer",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:s.text,ts:r.ts,keyText:String(r.text||"").slice(0,40),from:await Hy(s,Ai),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}if(await $(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch){let a=gy(r.text);if(a){let{offered:i,toPr:c}=by(a),l;if(t.workspace?.kind==="multi"){let u=await bs(t).catch(()=>[]);l=u.length?u.map(f=>({name:f.name,slug:f.repo??null})):void 0}await $(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:c$(),ts:r.ts,excerpt:a,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:i||void 0,toPr:c||void 0,repos:l}})}}}}catch{}}async function Gy(t,e){try{let n=await nd(t),r=[...e||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Wy(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await $(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),Ly(t,o),Fy(t,o))}}catch{}}async function Vy(t,e){try{let n=await nd(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=qy(o);n.keys.has(s)||(n.keys.add(s),await $(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}function l$(t){let e=[],n=null;for(let r of String(t||"").split(`
150
- `)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;e.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return e}var rd=new Map;async function u$(t,e){let n=`${t.id}:${e.name}`;if(!rd.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);rd.set(n,o)}return rd.get(n)}async function zy(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Vn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await u$(t,e),o=l$(await Ut(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await $(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:gn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await $(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}import _i from"node:fs";import sd from"node:path";import d$ from"node:os";var f$=process.env.BOSTRAT_STATE_DIR||sd.join(process.env.HOME||d$.tmpdir(),".bostrat"),Xy=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,p$="https://cursor.com/api/dashboard/get-filtered-usage-events",Jy=100;async function m$({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(p$,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${e}`},body:JSON.stringify({teamId:0,startDate:String(t),endDate:String(Date.now()),page:o,pageSize:Jy})});if(!s.ok)throw new Error(`cursor usage feed: HTTP ${s.status}`);let a=await s.json(),i=a?.usageEventsDisplay||a?.usageEvents||[];if(r.push(...i),i.length<Jy)break}return r}function h$(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}var w$=5*6e4,$n={at:0,rows:null,error:null},Ei=()=>sd.join(f$,"cursor-usage.json");function Yy(){try{return JSON.parse(_i.readFileSync(Ei(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function g$(t){try{_i.mkdirSync(sd.dirname(Ei()),{recursive:!0});let e=Ei()+".tmp";_i.writeFileSync(e,JSON.stringify(t)),_i.renameSync(e,Ei())}catch{}}var y$=5e3;async function b$({fetchImpl:t,now:e=Date.now()}={}){if($n.rows&&e-$n.at<w$)return $n.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Yy(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await m$({sinceMs:o,cookie:n,fetchImpl:t});$n={at:e,rows:s.map(h$).filter(Boolean),error:null}}catch(s){$n={at:e,rows:null,error:String(s?.message||s)}}return $n.rows}var ad=()=>({enabled:Xy(),lastPollAt:$n.at||null,error:$n.error}),od=new Map;async function k$(t,e){let n=`${t.id}:${e.name}`;if(!od.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);od.set(n,o)}return od.get(n)}async function Zy(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Xy())return;let r=await b$({fetchImpl:n});if(!r||!r.length)return;let o=await hg(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await k$(t,e),a=Yy(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${t.id}:${e.name}:${l.id}`))continue;s.add(l.id),i.add(`${t.id}:${e.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=gn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await $(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}g$({sinceMs:c,seen:[...i].slice(-y$)})}catch{}}import{execFile as A$}from"node:child_process";import{promisify as _$}from"node:util";import Qy from"node:fs/promises";import tb from"node:path";import x$ from"node:os";var eb=process.env.BOSTRAT_STATE_DIR||tb.join(process.env.HOME||x$.tmpdir(),".bostrat"),v$=tb.join(eb,"github-latency.ndjson"),S$=100,Ii=[],id=new Map;function Ri({change:t,repo:e=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:t,repo:e,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};Ii.push(l),Ii.length>S$&&Ii.shift();let u=id.get(t)||{count:0,measured:0,totalMs:0,maxMs:0};return u.count+=1,c!=null&&(u.measured+=1,u.totalMs+=c,u.maxMs=Math.max(u.maxMs,c)),id.set(t,u),T$(l),l}async function T$(t){try{await Qy.mkdir(eb,{recursive:!0}),await Qy.appendFile(v$,JSON.stringify(t)+`
151
- `)}catch{}}function nb(){let t={};for(let[e,n]of id)t[e]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:t,recent:Ii.slice(-20)}}async function E$(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([no("git",["-C",t,"rev-parse","HEAD"]),no("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await $({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var no=_$(A$),I$={timeout:3e4,maxBuffer:8*1024*1024};async function re(t,e,n={}){let{caller:r="prs",...o}=n,s=await me({cwd:t});Je(r);try{let{stdout:a}=await no("gh",e,{cwd:t,...I$,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw An(a)&&_n(a),a}}var $i=null;async function ud(){if($i&&Date.now()-$i.at<6e4)return $i.data;let t;try{Je("auth");let e=await no("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"||e?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await Ro();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return $i={at:Date.now(),data:t},t}async function R$(t){try{let{stdout:e}=await no("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function ob(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r=t.completedAt||t.createdAt||null,o;return t.__typename==="StatusContext"?o=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?o="pass":o="fail",{name:e,state:o,url:n,completedAt:r}}function cd(t){let e=(t||[]).map(ob),n=e.filter(s=>s.state==="pending").length,r=e.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=e.length&&!n&&r.length?Math.max(...r):null;return{total:e.length,passed:e.filter(s=>s.state==="pass").length,failed:e.filter(s=>s.state==="fail").length,pending:n,failing:e.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}var dd="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",sb=dd.replace("statusCheckRollup,","");function fd(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function ab(t,e){if(!e)return null;let n=await et(t);if(!n)return null;try{let r=await re(t,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(e)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function ib(t){return(t||[]).map(e=>({name:e.name,detailsUrl:e.html_url||null,status:String(e.status||"").toUpperCase(),conclusion:e.conclusion?String(e.conclusion).toUpperCase():null,completedAt:e.status==="completed"&&e.updated_at||null}))}async function cb(t,e){let n=await ab(t,e);return n===null?{...cd([]),unavailable:!0}:cd(ib(n))}function Oi(t,e=cd(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}var $$=9e4,eo=new Map;function O$(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){eo.delete(s);continue}r.add(s),eo.has(s)||eo.set(s,n),o.mergeabilityPending=n-eo.get(s)<$$}for(let o of eo.keys())o.startsWith(`${t}#`)&&!r.has(o)&&eo.delete(o);return e}async function ro(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await re(n.path,["pr","view",String(r),"--json",dd]);return Oi(JSON.parse(o))}catch(o){if(!fd(o))throw o;let s=await re(n.path,["pr","view",String(r),"--json",sb]),a=JSON.parse(s);return Oi(a,await cb(n.path,a.headRefOid))}}var rb=(t,e)=>t.number-e.number;async function xs(t,{caller:e="prs"}={}){try{let n=await re(t,["pr","list","--json",dd,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>Oi(r)).sort(rb)}catch(n){if(!fd(n))throw n;let r=await re(t,["pr","list","--json",sb,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Oi(o,await cb(t,o.headRefOid))))).sort(rb)}}var M$=3e4,C$=1500,ks=new Map;function Re(){ks.clear()}async function N$(t,e){if(!t)return e;for(let n of e){let r=await Bt(t,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await ei(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await ri(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function lb(){let t=await ud();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Oe())await R$(a.path)&&e.push(a);let n=Date.now(),r=ki(n)>0,o=await Promise.all(e.map(async a=>{let i=ks.get(a.path),c=i?.pending?C$:M$;if(i&&(r||n-i.at<c))return i.entry;try{let l=await et(a.path).catch(()=>null),u=O$(a.path,await xs(a.path,{caller:"tab"}),n);if(await N$(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(h=>h.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Ri({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return ks.set(a.path,{at:Date.now(),entry:f,pending:d}),f}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Ie(l)}}})),s=new Set(e.map(a=>a.path));for(let a of ks.keys())s.has(a)||ks.delete(a);return{auth:t,repos:o}}async function Xe(t){let e=(await Oe()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function fr(t){return Eo(t)}var vs=new Set(["squash","merge","rebase"]);async function oo(t,e,{method:n="squash"}={}){let r=await Xe(t);if(!vs.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(e);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await me({cwd:r.path})).GH_TOKEN){let a=await Sc().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await re(r.path,s)}catch(a){throw new Error(Ie(a))}finally{Re()}return{ok:!0,number:o}}async function Ss(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await re(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Ie(s))}finally{Re()}let o=await et(n.path).catch(()=>null);return o&&await Gw(o,r).catch(()=>{}),{ok:!0,number:r}}async function pd(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await re(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Ie(o))}finally{Re()}return{ok:!0,number:r}}var P$=2500,D$=9e4,B$=t=>new Promise(e=>setTimeout(e,t));function md(t){return t.green&&!t.isDraft}function j$(t){return t.isDraft?"draft":t.mergeable==="CONFLICTING"?`conflicts with ${t.baseRefName}`:t.checks.failed>0?"checks failing":t.checks.pending>0?"checks running":t.mergeable==="UNKNOWN"||t.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":t.mergeStateStatus==="BEHIND"?`behind ${t.baseRefName}`:t.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${t.mergeStateStatus})`}function ub(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:md(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function db(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var ld=new Map;function fb(){let t={};for(let[e,n]of ld)t[e]={...n};return t}async function pb(t,{method:e="merge",onMerged:n=null}={}){let r=await Xe(t);if(!vs.has(e))throw new Error(`invalid merge method: ${e}`);if(ld.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await xs(r.path)).filter(md).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:e,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return ld.set(r.path,s),F$(s,o,{listPrs:()=>xs(r.path),merge:a=>oo(r.path,a,{method:e}),sleep:B$,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function L$(t,e){let n=Date.now()+(e.settleMaxMs??D$),r=null;for(;;){let o=null;try{if(o=await e.listPrs(),r=null,!o.some(a=>t.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await e.sleep(P$)}}async function F$(t,e,n){try{let r=e;for(let o=0;o<e.length;o++){let s=e[o];if(t.current=s.number,o>0)try{r=await L$(e.slice(o).map(i=>i.number),n)}catch(i){for(let c of e.slice(o))t.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${Ie(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){t.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!md(a)){t.skipped.push({number:a.number,title:a.title,reason:j$(a)});continue}try{await n.merge(a.number),t.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){t.failed.push({number:a.number,title:a.title,reason:Ie(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Re()}}async function hd(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await no("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Ie(l)}`)}await E$(t);let o=n||"";if((await me({cwd:t})).GH_TOKEN){let l=await Sc().catch(()=>null);l&&(o=`${o?`${o}
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
152
 
153
- `:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",e.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await re(t,s)}catch(l){throw new Error(Ie(l))}finally{Re()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function mb(t,e){let n=await Xe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await re(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Ie(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!fd(i))throw i;let c=await re(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await ab(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Ie(i));o=ib(l).map(ob).filter(u=>u.state==="fail").map(u=>({name:u.name,link:u.url}))}let s=new Set,a=[];for(let i of o){let c=U$(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await re(n.path,["run","view",c,"--log-failed"]).then(u=>H$(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function U$(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function H$(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
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(`
154
154
  `);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
155
155
  `+o.slice(-e).join(`
156
156
  `)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
157
157
  `+r.slice(-n)),r}function Ie(t){return String(t?.stderr||t?.message||t||"").trim().split(`
158
- `).find(n=>n.trim())||"gh failed"}var W$=6e3,wd=new Map,q$=30*6e4;function gd(t,e=Date.now()){let n=null;for(let r of t||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&e-n.ts>q$?null:n}async function hb(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await B(t)}catch{return}if(gd(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await vi(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(wd.get(t.id)||0)<W$)return;wd.set(t.id,o);let s;try{s=await xs(t.cwd,{caller:"reconcile"})}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await et(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Jt({repo:i,number:a.number,trackId:t.id,url:a.url,headRef:e,title:a.title,source:"reconciled"});if(!l&&!(c?.trackId===t.id&&await cs(i,a.number))){await $(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"pushed",number:a.number,url:a.url,title:a.title,branch:e,repo:i}});return}}await $(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e,...i?{repo:i}:{}}}).catch(()=>{}),wd.delete(t.id)}import K$ from"node:fs/promises";import kb from"node:path";import G$ from"node:os";import{randomUUID as xb}from"node:crypto";var V$=process.env.BOSTRAT_STATE_DIR||kb.join(process.env.HOME||G$.tmpdir(),".bostrat"),vb=kb.join(V$,"flows.json"),wb=20,gb=80,yb=500,bb=80,Ye=null,yd=null;async function Ze(){return Ye||(yd||=(async()=>{let t=!0;try{Ye=JSON.parse(await K$.readFile(vb,"utf8"))}catch{Ye={flows:[]},t=!1}finally{yd=null}if(Ye.flows||=[],!t&&ht()){let{records:e,source:n}=await $t("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&Ye.flows.push(r.data);Ye.flows.length&&await so().catch(()=>{})}}return Ye})(),yd)}async function so(){await Ft(vb,Ye)}var Mi=t=>Pt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function bd(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Sb({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>wb)throw new Error(`steps must be an array of 1-${wb}`);let r=await Xn({trackId:t}),o=[...n],s=[];for(let i=0;i<e.length;i++){let c=e[i]||{},l=`step ${i+1}`,u=typeof c.caption=="string"?c.caption.trim():"";if(!u)throw new Error(`${l}: caption (string) required`);if(u.length>yb)throw new Error(`${l}: caption over ${yb} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>bb)throw new Error(`${l}: title over ${bb} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(v=>v.id===d);if(p.length||(p=r.filter(v=>v.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(v=>`mock:${v.id}`).join(", ")}) \u2014 use the id`);let m=p[0],h=null;if(c.v!=null&&(h=Number(c.v),!Number.isInteger(h)||!(m.versions||[]).some(v=>v.v===h)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(v=>v.mockId===m.id),k=y>=0?o.splice(y,1)[0].id:xb().slice(0,8);s.push({id:k,mockId:m.id,caption:u,title:f,v:h})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function z$(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>gb)throw new Error(`name over ${gb} chars`);return t.trim()}async function Tb({trackId:t}={}){let e=await Ze();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function J$(t){let n=(await Ze()).flows.find(r=>r.id===t);return n?{...n}:null}async function Ab(t){let e=await J$(t);if(!e)return null;let n=new Map((await Xn({trackId:e.trackId})).map(r=>[r.id,r]));return e.steps=e.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=ae(o,r.v);return{...r,missing:!1,mockName:o.name,mockState:o.state,servedV:s?.v??null,width:s?.width??null,height:s?.height??null}}),e}async function _b({trackId:t,name:e,steps:n,cwd:r,box:o,agent:s}){if(!t)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=z$(e),i=await Ze(),c=i.flows.find(p=>p.trackId===t&&p.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await Sb({trackId:t,steps:n}),f=Date.now(),d={id:xb().slice(0,8),trackId:t,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await so(),await bd(d,"created"),Mi(d),{...d}}async function Eb(t,{steps:e,note:n}={}){let o=(await Ze()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Sb({trackId:o.trackId,steps:e,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await so(),await bd(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Mi(o),{...o}}async function Ib(t){let e=await Ze(),n=e.flows.findIndex(o=>o.id===t);if(n===-1)throw new Error("flow not found");let r=e.flows[n];return e.flows.splice(n,1),await so(),await bd(r,"deleted"),oe("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Rb(t){return(await Ze()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function $b(t){let n=(await Ze()).flows.filter(r=>r.trackId===t.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=t.name||t.id,r.updatedAt=Date.now(),Mi(r);return n.length&&await so(),n.length}async function Ob(t){let e=await Ze(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await so();for(let r of n)oe("flows",r.id).catch(()=>{})}return n.length}async function Mb(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Ze();for(let r of n.flows)await Mi(r);return{pushed:n.flows.length,source:"plane"}}import kd from"node:fs/promises";import Ts from"node:path";import{randomUUID as X$}from"node:crypto";var Nb=".bostrat/uploads",xd=t=>Ts.join(t,Nb),Cb=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,Y$=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Z$=new Set(["pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","rtf","txt","md","markdown","rst","csv","tsv","json","jsonl","yaml","yml","xml","html","htm","log","ini","toml","conf","env","js","jsx","ts","tsx","py","rb","go","rs","java","kt","c","h","cpp","cc","cs","php","swift","sh","bash","zsh","sql","css","scss"]);function vd(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return Y$.has(e)?"image":Z$.has(e)?"doc":null}function Q$(t){let r=Ts.basename(String(t||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function Pb(t,{filename:e,buffer:n}){if(!t)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>Cb)throw new Error(`file too large \u2014 max ${Math.floor(Cb/(1024*1024))} MB`);let r=Q$(e),o=Ts.extname(r).slice(1),s=vd(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=X$().slice(0,8),i=xd(t);await kd.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await kd.writeFile(Ts.join(i,c),n,{mode:384}),await kd.chmod(Ts.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${Nb}/${c}`,kind:s,size:n.length}}function Db(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var tO=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Sd(t){return t.role==="user"&&tO.test((t.text||"").trim())}function pr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var rO=nO(eO),Qe=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),qb=U.join(Qe,"tracks.json"),oO=U.join(Qe,"events.jsonl"),$d=U.join(Qe,"workspaces"),sO=process.env.BOSTRAT_DEFAULT_REPO||null,Di={claude:{envVar:"BOSTRAT_AGENT_CMD",base:"claude",skipFlag:"--dangerously-skip-permissions"},cursor:{envVar:"BOSTRAT_CURSOR_CMD",base:"cursor-agent",skipFlag:"--force"},codex:{envVar:"BOSTRAT_CODEX_CMD",base:"codex",skipFlag:"--dangerously-bypass-approvals-and-sandbox"}},Bi=Object.keys(Di);function aO(t,{skipPermissions:e=!0}={}){let n=Di[t];if(!n)throw new Error(`unknown agent "${t}"`);let r=process.env[n.envVar];return r||(e&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function Kb(t,e,{skipPermissions:n=!0}={}){let r=Di[t];if(!r)throw new Error(`unknown agent "${t}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(t){case"claude":return`${r.base} --resume ${e}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function Cs(t){let e=Di[t];if(e)return process.env[e.envVar]||e.base}var On={at:0,value:null};async function Cd({maxAgeMs:t=6e4}={}){if(On.value&&Date.now()-On.at<t)return On.value;let e=On.value,n=await Promise.all(Bi.map(async r=>{let o=await wa(Cs(r),{retries:[]});return o===Ar?[r,e?.[r]??!0]:[r,o===ha]}));return On.value=Object.fromEntries(n),On.at=Date.now(),On.value}async function ji(t,e=!0){let n=aO(t,{skipPermissions:e});if(await wa(n)===Tr){On.at=0;let r=n.split(/\s+/)[0],o=process.env.FLY_APP_NAME?"This Fly node's image predates CLI parity \u2014 update the node image (Links page \u2192 Fly card \u2192 Update) or re-provision.":Wc[r]?`Install it on this node: ${Wc[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). ${o}`)}return n}var Li=".bostrat/handoff/latest.md",iO=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;function cO(t,e=Li){return`${t.trim()}
158
+ `).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()}
159
159
 
160
- (Read ${e} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}var lO=/\(Read \S{1,300} for handoff context from the prior lead — you are now the lead; none of the prior chat is in your context\. Then respond to the user above\.\)$/;function Bb(t){return t.role==="user"&&lO.test((t.text||"").trim())}var Ci=".bostrat/handoff/subtask-result.md",_s=".bostrat/handoff/lead-context.md",uO=(t,e=!1)=>e?`You are a SUBTASK agent working in an isolated multi-repo workspace, delegated by a lead agent on a shared workstream. This directory is NOT itself a git repo: each subdirectory is an isolated git worktree of one of the workstream's repos, already seeded with the lead's full working tree for that repo. The conversation so far is summarized in ${_s} \u2014 read that first for context. Your scoped task:
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:
161
161
 
162
162
  ${t}
163
163
 
164
- Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${Ci} (at this directory's root) with sections \u2014 Result (what you did), Files (paths you changed, prefixed with the repo directory), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`:`You are a SUBTASK agent working in an isolated git worktree, delegated by a lead agent on a shared workstream. The lead's full working tree is already checked out here, and the conversation so far is summarized in ${_s} \u2014 read that first for context. Your scoped task:
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:
165
165
 
166
166
  ${t}
167
167
 
168
- Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${Ci} with sections \u2014 Result (what you did), Files (paths you changed), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`,dO=(t,e,n,r)=>`A subtask box you delegated has finished: "${t}". Its changes are now merged into your working tree (${e.files} file${e.files===1?"":"s"}, +${e.insertions} \u2212${e.deletions}). Read ${r} for exactly what it did. `+(n.length?`IMPORTANT: ${n.length} merge conflict${n.length===1?"":"s"} need resolving \u2014 fix the conflict markers in: ${n.join(", ")}, then continue.`:"Review the merged changes and continue the work."),$e=null,Td=null;async function H(){return $e||(Td||=(async()=>{try{$e=JSON.parse(await F.readFile(qb,"utf8"))}catch{$e={tracks:[]}}finally{Td=null}return $e})(),Td)}async function V(){await Ft(qb,$e)}var Z=(t,e)=>t.tracks.find(n=>n.id===e)||null,Ad=Promise.resolve();function fO(t,e){let n=JSON.stringify({trackId:t,...e})+`
169
- `;return Ad=Ad.then(()=>F.mkdir(Qe,{recursive:!0})).then(()=>F.appendFile(oO,n)).catch(()=>{}),Ad}var dt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),fO(t.id,r),ly(t,r),r};Lp(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await H()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(dt(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await V().catch(()=>{}))});var _d=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),pO=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function Gb(t){if(!t)return null;let e=typeof t=="string"?[t]:Array.isArray(t?.codes)&&t.codes.length?t.codes:t?.code?[t.code]:[],n=[...new Set(e.filter(s=>pO.has(s)))];if(!n.length)return null;let r=typeof t=="object"?t?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function Vb(t,e,n,{kind:r}={}){let o=await tt(e);if(!o)throw new Error(`box "${e}" is not running`);let s=Cs(o.agent);await pn({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await Et(e,n,{...Pe(o.agent),kind:r||"orchestration"}),await q({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await H(),i=Z(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);dt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await V()}var mr=new Set(["merged","conflict","canceled","failed","handed-off"]),jb=new Map;async function mO(t){let e=U.join(Ra(t),`${t.id}.ndjson`),n;try{n=await F.stat(e)}catch{return null}let r=jb.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return jb.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function hO(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Xu(n.name,n.agent)}async function tn(t,e=null){let n=e??new Map((await Ce()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=mr.has(i.state)?i.state:c?i.role==="lead"?"live":i.state:"stopped";return{...i,state:l,cwd:c?.cwd||i.cwd,agent:c?.agent||i.agent,sessionId:c?.sessionId||i.sessionId}}),[o,{working:s,prompted:a}]=await Promise.all([mO(t),hO(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Ni.has(t.id)}}var wO=5e3;function gO(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),f=l.createdAt-wO,d=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=f&&m.birthMs<d);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function zb(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(d=>d.box.sessionId),a=Cp(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=gO(u,a,d=>i.has(d));for(let d of f){let p=u.find(m=>m.name===d.box);p&&(p.sessionId=d.sessionId),await Tp(d.box,d.sessionId),n=!0}}return n}async function st(){let t=await H();await zb(t)&&await V();let e=new Map((await Ce()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>tn(n,e)))}async function Ai(t,e){let n=await H();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function Jb(t){let e=Ao(t);if(!e)return null;let n=await H();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Rf(o.hookTokenHash,e))return{track:r,box:o};return null}async function Q(t){let e=await H(),n=Z(e,t);return n?(await zb(e)&&await V(),tn(n)):null}var yO={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function Lb(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(yO[t.agent]||[]).some(n=>n.test(e))}var bO=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function Fb(t){let e=(t.text||"").trim();return e?bO.some(n=>n.test(e)):!1}var kO=10*6e4;async function Nd(t,e={}){let n=await Q(t);if(!n)return null;let r=new Set([...n.events.filter(b=>b.type==="orchestration-prompt"&&b.prompt).map(b=>String(b.prompt).trim()),...n.events.filter(b=>b.type==="said"&&b.text).map(b=>String(b.text).trim())]),o=Ns(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=Fl(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Gh(n),...l],d={windows:[...eh(n),...nh(n,Pa),...a,...u]},m=(await Promise.all(n.boxes.map(b=>_u(b,e)))).flat(),h=m.map(b=>{let _={kind:"message",...b};if(_.role==="assistant"&&(_.text||"").trim()&&_.tools?.length){let lt=ps(_.agent).cliTools,nt=_.tools.filter(bt=>!lt.has(bt));return{..._,tools:nt.length?nt:void 0}}return _}).map(b=>{if(b.role!=="user")return b;let _=Ma(b.text);return _?_.kind==="suppress"?null:{...b,note:_,text:_m(_)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!Lb(b)).filter(b=>!Fb(b)).filter(b=>!Bb(b)).filter(b=>!Sd(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!rh(b,d)),y=h.filter(b=>b.failed&&b.text===hl);if(y.length){let b=new Map;for(let _ of y){if(!b.has(_.box)){let bt=[...(await mt(_.box,{lines:_t}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(jt=>jt[1]);b.set(_.box,bt.length?bt[bt.length-1]:null)}let lt=b.get(_.box);lt&&(_.text=`\u25A0 ${lt}`)}}let k=await Promise.all(n.boxes.map(b=>kg(b,e)));await Vy(n,k.flat()),await Ky(n,h),await uy(n),await Qh(n,h).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await zy(n,b),b.agent==="cursor"&&b.state==="live"&&await Zy(n,b);let v=await Promise.all(n.boxes.map(b=>bg(b,e)));await Gy(n,v.flat().filter(b=>!_d(b.box,b.ts,u)));let I=await B(n),R=c&&i.viewId?i.viewId:null,z=ky(I),O=zn(I).filter(b=>!(b.kind==="message"&&(Lb(b)||Fb(b)||Bb(b)||Sd(b)))&&!(b.kind==="view-card"&&R&&b.viewId===R)),M=xy(O,z),S=O.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!M.has(b)&&_d(b.box,b.ts,z))),N=Al(I,S).map(b=>({kind:"command-batch",id:b.id,ts:b.pending?b.ts:(b.anchorTs??b.ts)-1,anchorTs:b.anchorTs,anchorBox:b.anchorBox,agent:b.agent,boxRole:b.boxRole,commands:b.commands,pending:b.pending})).filter(b=>!_d(b.anchorBox,b.anchorTs??b.ts,u)),D=Cm(Nm([...S,...N].filter(b=>b.ts!=null).sort((b,_)=>b.ts-_.ts))),it=xO(n),ct=it.size?D.filter(b=>b.kind==="event"||!it.has(b.box??b.anchorBox)):D,T=n.boxes.find(b=>b.name===n.leadBox)||null,g=T?await jo(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!g&&T){let b=[...I].reverse().find(_=>_.type==="context.usage"&&_.boxId===T.name);b?.payload?.window&&(g={pct:b.payload.pct??(b.payload.occupancy!=null?b.payload.occupancy/b.payload.window:null),occupancy:b.payload.occupancy??null,window:b.payload.window,windowKnown:!0,model:b.payload.model||null})}let w=[...m].reverse().find(b=>b.role==="assistant"&&b.model),A=w?.model||null,E=[...n.events||[]].reverse().find(b=>b.type==="model-change"),J=g?.model||A;E&&(!w?.ts||E.at>=w.ts)&&(J=E.to);let W=T?await mt(T.name,{lines:_t}).catch(()=>""):"",at=T&&Vr(T.agent,W)?await dn(T.name).catch(()=>W):W,Nt=T?lr(T,at):null;!Nt&&T&&!Fo(T.agent,W)&&!Od(n.id).length&&(Nt=Tl(ct,{leadBoxId:T.name,provider:T.agent,isAnswered:b=>I.some(_=>_.type==="prompt.answered"&&_.payload?.promptId===b)})),Nt&&dy(n,T,Nt);let Tt=ct.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),gr=!!T&&xi(T.name,T.agent,W)&&!Nt;T&&!T.readyAt&&(gr||Nt||W&&Ta(T.agent,W))&&(T.readyAt=Date.now(),await V());let Ys=!!T&&!T.readyAt&&Date.now()-(n.createdAt||0)<kO;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&Ta(T.agent,W)&&!Nt){let b=await H(),_=Z(b,n.id);_?.setup&&!_.setup.connectedAt&&(_.setup.connectedAt=Date.now(),n.setup.connectedAt=_.setup.connectedAt,await $(_,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:_.setup.connectedAt}}).catch(()=>{}),await V())}let G=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,j=null;if(c){let b=Date.now()-i.startedAt;if(i.viewId&&!gr){let _=Ua(n.id);_&&(dt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:_.finishedAt}),await V())}else if(Nt||b>qh&&!gr&&!i.viewId||b>Ll){let _=Nt?"needs-input":b>Ll?"timeout":"no-view";Ua(n.id,{error:_})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:_}}).catch(()=>{})}else j={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Tt,context:g?{pct:g.pct,occupancy:g.occupancy,window:g.window,windowKnown:g.windowKnown,model:g.model}:null,models:{current:J,options:oy(T?.agent)},pendingPrompt:Nt,working:gr,stopping:$O(n.id),leadStarting:Ys,handoff:G,view:j,queued:Od(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function xO(t){let e=new Set;for(let n of t.boxes||[])n.role==="subtask"&&n.name&&e.add(n.name);for(let n of Object.values(t.subtasks||{}))n?.box&&e.add(n.box);return e}async function Xb(t,e){let n=await Q(t);if(!n)return null;let r=ho(t).find(f=>f.id===e)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await B(n),a=zn(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Al(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await mt(o,{lines:_t}).catch(()=>"");u=xi(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Pd(t,{promptId:e,answers:n,text:r}={}){let o=Z(await H(),t);if(!o)throw new Error(`track "${t}" not found`);Ui(t,"prompt-answer");let s=o.boxes.find(d=>d.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await tt(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Cu(s);if(i||(i=await vO(o,s)),!i)throw new Error("no pending prompt to answer");if(e&&e!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let d=typeof r=="string"?r.trim():"";if(!d)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(d)||d.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Et(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await At(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=$m(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await ue(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=Og(i,n||[]);await At(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=Gp(i,n||[]);await Et(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:d,count:1}}if(i.answerMode==="say"){let d=i.questions||[];for(let p=0;p<d.length;p++){let m=d[p],h=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));zu(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:p,questionTotal:i.questionTotal??d.length,header:m.header||"",question:m.question||"",multiSelect:!!m.multiSelect,chosen:h})}return await q({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(d=>l.options?.[d]).filter(Boolean).map(d=>({label:d.label,description:d.description||""})),f=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:u,...f?{control:f}:{}},await q({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),zu(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function vO(t,e){if(!e)return null;let n=await B(t).catch(()=>[]);return Tl(zn(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Yb(t,{promptId:e,questionIndex:n}={}){let r=Z(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await tt(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Cu(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(e&&e!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=Mg(a,n);return c.length?(await At(r.leadBox,c,{kind:"navigate-prompt"}),await q({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}var SO=1500,Es=new Map;function Dd(t){let e=Es.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},Es.set(t,e)),e}function Od(t){return(Es.get(t)?.items??[]).map(e=>({...e}))}function Fi(t){let e=Es.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}async function Zb(t){let e=Fi(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=Z(await H(),t);if(o)return r=uo(o,n),await V(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function Qb(t){let e=Dd(t);e.timer||(e.timer=setInterval(()=>TO(t).catch(()=>{}),SO))}async function TO(t){let e=Es.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Z(await H(),t);if(!n||n.archivedAt){Fi(t);return}let r=Ns(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){pr(e,!1);return}let o=await tt(n.leadBox);if(!o){pr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(Zr(s?.agent)){pr(e,!1);return}let a=await mt(n.leadBox,{lines:_t}).catch(()=>"");if(Sa(s?.agent,a)){pr(e,!1);return}if(Vr(s?.agent,a)){let c=await dn(n.leadBox).catch(()=>a);if(lr(s,c)){pr(e,!1);return}}if(!pr(e,!0))return;let i=e.items.shift();e.idleSince=null,await ek(n,s,o,i.text,i.kind??null,i.attachments??null),await V()}finally{e.flushing=!1}}}async function tk(t,e,n,r){let o=Zr(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(uo(t,[n]),await V()),zg(t.id,o))try{await $(t,{type:"usage.cap.hit",actor:"system",provider:e?.agent??null,boxId:t.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${e?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function ek(t,e,n,r,o=null,s=null){await tk(t,e,r,o);let a=Is.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=cO(r,t.pendingHandoffHydrate.artifact||Li),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=Db(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
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()}
170
170
 
171
- ${f}`:f}let u=dt(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...l.length?{attachments:l}:{}});lo(t,null);try{if(await pn({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:Cs(n.agent)}),(Is.get(t.id)||0)!==a){!o&&r.trim()&&(uo(t,[r]),await V());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}await Et(t.leadBox,i,{...Pe(e?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){throw dt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,...f?.interrupted?{reason:"interrupted"}:{}}),f}await FO(t,e,i)||(await sk(t.leadBox,e?.agent,i.length+8).catch(()=>{}),dt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,reason:"unsubmitted"}),o||uo(t,[r])),c&&await q({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function nk(t,{actions:e}={}){Dd(t).items.push({id:Ms(),text:vy(e),ts:Date.now(),kind:"ship-notify"}),Qb(t)}async function ue(t,e,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(d=>d&&typeof d.rel=="string"):[];if((typeof e!="string"||!zr(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Z(await H(),t);if(!s)throw new Error(`track "${t}" not found`);if(s.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before driving it`);let a=s.boxes.find(d=>d.name===s.leadBox),i=zr(e);await tk(s,a,i,n),Ui(t,"say");let c=await tt(s.leadBox);if(!c&&await Fd(s)&&(await V(),c=await tt(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Dd(t),u=await mt(s.leadBox,{lines:_t}).catch(()=>""),f=l.items.length>0||Sa(a?.agent,u);if(!f&&Vr(a?.agent,u)){let d=await dn(s.leadBox).catch(()=>u);f=!!lr(a,d)}if(f){let d={id:Ms(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),Qb(t),lo(s,null),await V(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await ek(s,a,c,i,n,o),await V(),{ok:!0,leadBox:s.leadBox}}var Ub="Spin up a live preview View of this app so I can see it on another device. From the repo root, run `bostrat-view up --name \"<short name>\" -- <serve command>` \u2014 pick the right dev/serve command for this repo yourself (e.g. `npm run dev`), pass $PORT through if the command needs an explicit port, and share the public URL it prints. If a suitable View is already live, just re-share its URL instead of starting another. Don't paste a localhost link \u2014 it's dead on my device.\n\nIMPORTANT: if you cannot confidently identify WHICH project/app to serve, or which serve command to use (e.g. several apps in this tree, or no obvious dev script), do NOT guess and do NOT run `bostrat-view up`. Instead, reply with a short question asking me which one you should preview, and stop.";async function rk(t,{hint:e}={}){let n=Z(await H(),t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it first`);let r=Fl(n.id);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o=n.boxes.find(i=>i.name===n.leadBox),s=String(e||"").trim()?String(e).trim().slice(0,40):"preview";Kh(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${Ub}
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}
172
172
 
173
- What to preview: ${String(e).trim()}`:Ub;try{await ue(t,a,{kind:"spin-up-view"})}catch(i){throw Ua(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Bd(t){let e=Z(await H(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);Is.set(t,(Is.get(t)||0)+1);let n=Fi(t),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=co.get(t);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=uo(e,r),await V()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:e.draftRev||0}}let s=OO(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Mn(AO)]);let a=Z(await H(),t)||e;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}var AO=4e3,_O=3,EO=[300,350,400],IO=1e3,RO=12,co=new Map,Is=new Map;function $O(t){let e=co.get(t);return!!e&&!e.canceled}function Ui(t,e){let n=co.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function ok(t){Ui(t,"track-ended"),Is.delete(t),Fi(t)}function OO(t,{droppedCount:e,droppedTexts:n}){let r={startedAt:Date.now(),clicks:1,escSends:0,bgSends:0,queuedTexts:[...n],canceled:!1,cancelReason:null,result:null,restored:null},o;return r.settled=new Promise(s=>{o=s}),co.set(t,r),(async()=>{let s=Z(await H(),t),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await tt(s.leadBox):null,c=!1,l=!1;if(i){t:for(let y=0;y<_O&&!r.canceled;y++){await At(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of EO){await Mn(k);let v=await mt(s.leadBox,{lines:_t}).catch(()=>"");if(!Fo(a?.agent,v)){c=!0;break t}}}if(!c){l=!0;for(let y=0;y<RO&&!r.canceled&&(await Mn(IO),!r.canceled);y++){let k=await mt(s.leadBox,{lines:_t}).catch(()=>"");if(!Fo(a?.agent,k)){c=!0;break}await At(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=Z(await H(),t)||s,f=u?.boxes.find(y=>y.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await PO(u,f).catch(()=>null),d||(p=await jO(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await CO(u).catch(()=>null):null,h=[d||p,...r.queuedTexts].filter(Boolean);u&&h.length&&(r.restored=uo(u,h),await V()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await MO(u||s,f,{ts:r.startedAt,result:r.result,dropped:e,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await q({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:t,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),co.get(t)===r&&co.delete(t),o()}),r}async function MO(t,e,n){await $(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}async function CO(t){let e=gd(await B(t));return e?.offerId?(await $(t,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,action:"dismiss",actions:null,reason:"interrupt"}}),e.offerId):null}var NO=[250,400,500,600];async function PO(t,e){if(e?.agent!=="claude")return null;let n=[...t.events||[]].reverse().find(r=>r.type==="said"&&r.text);if(!n)return null;for(let r of NO){await Mn(r);let o=await mt(t.leadBox,{lines:_t}).catch(()=>""),s=Go(e.agent,o);if(s&&wl(n.text,s))return await sk(t.leadBox,e.agent,n.text.length+8),dt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),n.text}return null}var DO=3e4,BO=[350,650,900];async function jO(t,e){if(e?.agent!=="claude")return null;let n=t.events||[],r=[...n].reverse().find(s=>s.type==="said"&&s.text);if(!r||r.kind||Date.now()-r.at>DO||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!vt(r.text))return null;for(let s of BO){await Mn(s);let a;try{a=await _u(e,{})}catch{return null}if(bm(a,r))return null;let i=await mt(t.leadBox,{lines:_t}).catch(()=>"");if(Go(e.agent,i))return null}return dt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}var LO=[250,450,700],Ed=2;async function FO(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=Ed;r++){let o=!1;for(let s of LO){await Mn(s);let a=await mt(t.leadBox,{lines:_t}).catch(()=>""),i=km(e.agent,n,a,{busy:Fo(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Ed&&(await At(t.leadBox,["Enter"],{kind:"submit-retry"}),q({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return q({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:Ed,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function sk(t,e,n){for(let r=0;r<3;r++){await ya(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Mn(200);let o=await mt(t,{lines:_t}).catch(()=>""),s=Go(e,o);if(!s)return!0;n=s.length+8}return!1}function lo(t,e,{source:n}={}){t.draftRev=(t.draftRev||0)+1;let r=typeof e=="string"?e:"";return r.trim()?t.draft={text:r,updatedAt:Date.now(),...n?{source:n}:{}}:delete t.draft,t.draft||null}function uo(t,e){let n=[],r=o=>n.some(s=>vt(s).includes(vt(o)));for(let o of[t.draft?.text,...e])o&&o.trim()&&!r(o)&&n.push(o);return n.length?lo(t,n.join(`
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(`
174
174
 
175
- `),{source:"restore"})?.text??null:null}var UO=12e4;function HO(t,e){let n=vt(e);if(!n)return!1;if(Od(t.id).some(o=>vt(o.text)===n))return!0;let r=t.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>UO)break;if(!(s.type!=="said"||!s.text||vt(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function ak(t,e,{rev:n=null}={}){if(typeof e!="string")throw new Error("text (string) required");let r=Z(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=e,s=zr(e);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&HO(r,s))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&!o.trim()&&r.draft?.text)return{ok:!0,draft:r.draft,rev:r.draftRev||0,ignored:"stale-clear"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let i=r.draft.text;vt(o).includes(vt(i))?lo(r,o):lo(r,`${i}
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}
176
176
 
177
- ${o}`,{source:"restore"})}else lo(r,o);return await V(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function ik(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Z(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await tt(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(f=>f.name===r.leadBox)?.agent||o.agent||"claude",i=sy(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await jo(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await pn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Cs(o.agent)});let u=await ay(a,r.leadBox,i);return dt(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await V(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function ck(t){for(let e of t){if(!e||!await li(e))continue;let n=await Ng(e).catch(()=>null);if(!n)continue;if((await F.stat(U.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function qt(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:U.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function bs(t){let e=[];for(let n of qt(t?.workspace)){if(!n?.dir)continue;let r=await Bu(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await ju(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function lk(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await Eo(e)??await Ac(e));let n=await ck([e]);if(!n)throw new Error(`tagged repo "${t}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function WO(t,e,n,r){let o=`track/${Wd(e)}-${t}`,s=[];try{await F.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await lk(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await et(c).catch(()=>null),u=U.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${U.basename(c)}-${p}`;a.add(u);let f=U.join(n,u),d=await ui(c);await Jr(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await Hi(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await ne(i.base,i.dir).catch(()=>{});throw await F.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var uk=t=>({branch:t.branch,kind:t.kind,repos:(t.repos??[]).filter(Boolean).map(({name:e,base:n,baseRef:r,repo:o})=>({name:e,base:n,baseRef:r,repo:o}))});async function dk(t,e){let n=await H(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await lk(typeof e=="string"?e.trim():e);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await et(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=U.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${U.basename(s)}-${d}`;let l=U.join(o.dir,c),u=await ui(s);await Pg(s,o.branch)?await Dg(s,l,o.branch):await Jr(s,l,o.branch,u);let f={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),f],await Hi(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"add",repo:a??s,name:c,workspace:uk(o)}),await V(),f}async function fk(t,e){let n=await H(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===e||c.repo===e||c.base===e));if(!s)throw new Error(`repo "${e}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of le.values())if(c.trackId===t&&!c.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before untagging");let a=await Bu(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await ju(s.dir,s.baseRef).catch(()=>0);if(a||i>0)throw new Error(`"${s.name}" has work (${a?"uncommitted changes":`${i} commit${i===1?"":"s"} ahead of ${s.baseRef??"base"}`}) \u2014 ship or discard it first`);return await ne(s.base,s.dir).catch(()=>{}),await F.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Hi(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:uk(o)}),await V(),{removed:s.name}}async function qO(t,e,{repo:n,repos:r,cwd:o}={}){await F.mkdir($d,{recursive:!0});let s=U.join($d,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return WO(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Eo(n)??await Ac(n));let i=await ck([n,o,sO]);if(i){let c=`track/${Wd(e)}-${t}`;try{let l=await ui(i);await Jr(i,s,c,l),await Md(s);let u=await et(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await F.mkdir(s,{recursive:!0}),await Bg(s),await Md(s),{dir:s,base:null,branch:null,kind:"repo"}}var Rs="# bostrat workspace",pk=`${Rs}
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}
178
178
 
179
179
  TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
180
180
  before substantive work \u2014 it is the shared cross-provider memory for this repo
@@ -352,9 +352,9 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
352
352
  statement, or omitted entirely renders no controls \u2014 so close EVERY completed
353
353
  implementation turn this way, every time. When the operator has already asked you
354
354
  to commit / push / open the PR, just do it; do not re-offer.
355
- `,KO=pk,As=pk+Om;async function Md(t){try{await F.writeFile(U.join(t,"AGENTS.md"),As,{flag:"wx"})}catch{return}await zO(t,["/AGENTS.md"])}var Id=t=>typeof t=="string"&&(t===Rs||t.startsWith(`${Rs}
356
- `)),GO="# bostrat multi-repo workspace";function VO(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
357
- `);return`${GO}
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
358
 
359
359
  This directory is NOT a git repo. It is a bostrat Track workspace holding one
360
360
  git worktree per tagged repo, ALL on the same branch \`${e}\`:
@@ -366,25 +366,25 @@ push / open PRs) in each repo separately, always on that shared branch. Work
366
366
  only needs to land in the repos it actually touches \u2014 an untouched repo's
367
367
  branch simply stays at its fork point and never gets pushed. When asked to run
368
368
  or preview an app, run it from that app's own repo directory.
369
- `}async function Hi(t,e,n){let r=VO(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await F.writeFile(U.join(t,o),r).catch(()=>{})}var Rd=As.replace(`${Rs}
370
- `,`${Rs}
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}
371
371
 
372
372
  (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
373
373
  SCOPE: these instructions apply ONLY when your working directory is under
374
374
  ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
375
375
  When working anywhere else, ignore everything below.
376
- `);async function mk(t){let{stdout:e}=await rO("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function zO(t,e){try{let n=await mk(t);await F.mkdir(U.dirname(n),{recursive:!0});let r=await F.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
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
377
  `).includes(s));o.length&&await F.appendFile(n,(r.endsWith(`
378
378
  `)||!r?"":`
379
379
  `)+o.join(`
380
380
  `)+`
381
- `)}catch{}}async function JO(t,e){try{let n=await mk(t),r=await F.readFile(n,"utf8"),o=r.split(`
381
+ `)}catch{}}async function XO(t,e){try{let n=await mk(t),r=await F.readFile(n,"utf8"),o=r.split(`
382
382
  `).filter(s=>s!==e);o.length!==r.split(`
383
383
  `).length&&await F.writeFile(n,o.join(`
384
- `))}catch{}}async function hk(){let t={refreshed:0,removed:0,codexGlobal:null};try{await F.mkdir(Qe,{recursive:!0}),await F.writeFile(U.join(Qe,"CLAUDE.md"),KO).catch(()=>{}),await F.writeFile(U.join(Qe,"AGENTS.md"),As).catch(()=>{});let e=process.env.CODEX_HOME||U.join(process.env.HOME||"/tmp",".codex");if((await F.stat(e).catch(()=>null))?.isDirectory()){let r=U.join(e,"AGENTS.md"),o=await F.readFile(r,"utf8").catch(()=>null);o===null?(await F.writeFile(r,Rd,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Id(o)&&o!==Rd&&(await F.writeFile(r,Rd).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[$d,fo]){let o=await F.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=U.join(r,s.name),i=U.join(a,"AGENTS.md"),c=await F.readFile(i,"utf8").catch(()=>null);c!==null&&Id(c)&&c!==As&&(await F.writeFile(i,As).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await F.readFile(l,"utf8").catch(()=>null);u!==null&&Id(u)&&(await F.rm(l,{force:!0}).catch(()=>{}),await JO(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function XO(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of qt(e))n.base&&n.dir&&await ne(n.base,n.dir).catch(()=>{});await F.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await ne(e.base,e.dir):await F.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function jd({name:t,cwd:e,repo:n,repos:r,agent:o="claude",skipPermissions:s=!0}){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");let a=await ji(o,s),i=await H(),c=Ms().slice(0,8),l=await qO(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(h=>h.name))),f=`${t.trim()} lead`;u.has(qc(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await qn(f,l.dir,{agentCmd:a,agent:o});Aa({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a});let p={id:c,name:t.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[Ak(d,"lead","live")],events:[],createdAt:Date.now()};return dt(p,"opened",{agent:o,box:d.name,skipPermissions:s,name:p.name,cwd:p.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:m,base:h,baseRef:y,repo:k})=>({name:m,base:h,baseRef:y,repo:k}))}:{}}}),i.tracks.push(p),await V(),tn(p)}var YO=new Set(["claude","codex","cursor"]);async function wk({provider:t="claude",fresh:e=!1}={}){if(!YO.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await H()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await tn(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await mo(r.id).catch(()=>{})}let o=(await ji(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Ms().slice(0,8),a=U.join(Qe,"setup",s);await F.mkdir(a,{recursive:!0});let i=await qn(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&Aa({cwd:i.cwd,sessionName:i.name,agent:t,agentCmd:o});let c={id:s,name:`Connect ${t}`,kind:"setup",setup:{provider:t,startedAt:Date.now(),connectedAt:null},cwd:i.cwd,workspace:{dir:a,base:null,branch:null,kind:"scratch"},leadBox:i.name,boxes:[Ak(i,"lead","live")],events:[],createdAt:Date.now()};return dt(c,"opened",{agent:t,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await H()).tracks.push(c),await V(),tn(c)}async function gk(t,e,n){await Zh(t).catch(()=>{});let r=$s.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),$s.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of ho(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Kd(a).catch(()=>{}),le.delete(a.id);for(let a of Object.values(e.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await H(),s=new Set((o.tracks||[]).filter(a=>a.id!==t&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of e.boxes)s.has(a.name)||await we(a.name).catch(()=>{})}async function yk(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await H(),o=r.tracks.findIndex(a=>a.id===t);if(o===-1)throw new Error(`track "${t}" not found`);let s=r.tracks[o];return e||await Ch(s),ok(t),await gk(t,s,"track deleted"),n?await lw(t).catch(()=>{}):await cw(t).catch(()=>{}),await cy(),await XO(s),e&&await Nh(t).catch(()=>{}),e?await Ob(t).catch(()=>{}):await $b(s).catch(()=>{}),dt(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await V(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}var Ni=new Set;async function mo(t){let e=await H(),n=Z(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);return n.archivedAt=Date.now(),ok(t),dt(n,"archived",{boxes:n.boxes.length}),await V(),n.kind==="setup"?(await bk(t),tn(n)):(Ni.add(t),QO(t).catch(()=>{}).finally(()=>Ni.delete(t)),{...await tn(n),archiving:!0})}var ZO=9e4;async function QO(t){try{await tM(t)}catch{}await bk(t).catch(()=>{})}async function bk(t){let e=await H(),n=Z(e,t);if(!(!n||!n.archivedAt)){await gk(t,n,"track archived");for(let r of n.boxes)mr.has(r.state)||(r.state="stopped");await V()}}async function tM(t){let e=await H(),n=Z(e,t);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await tt(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Qm(n.leadBox,i.cwd,{timeoutMs:ZO})),s="lead-cli"}catch{}if(r||(r=wu(n,await B(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await $(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await V()}async function kk(){let t=await H(),e=0,n=0,r=0;for(let o of t.tracks){if(!o.archivedAt||o.kind==="setup"||o.beat?.text){n++;continue}let s=wu(o,await B(o).catch(()=>[]));if(!s){r++;continue}o.beat={text:s,source:"derived",at:Date.now()},await $(o,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",payload:{text:s,source:"derived",backfill:!0}}).catch(()=>{}),e++}return await V(),{recorded:e,skipped:n,empty:r}}async function In(t,e){let n=await Bt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await xk(n);if(r.length){for(let o of r)await rr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await mo(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function xk(t){return(await Ee().catch(()=>[])).filter(n=>n.trackId===t.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===t.repo&&n.number===t.number))}async function Rn(t,e){return Ld(t,e,"merged")}async function Ld(t,e,n="merged"){let r=await Bt(t,e).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await xk(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Jw(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await ue(r.trackId,eM(a),{kind:"merge-notify"}),!0}catch{return!1}}var Hb={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function eM(t){let e=i=>`${i.repo?`${i.repo}`:""}#${i.number}${i.title?` "${i.title}"`:""}${i.url?` (${i.url})`:""}`;if(t.length===1){let i=t[0],c=`#${i.number}${i.title?` "${i.title}"`:""}`;return i.outcome!=="merged"?`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} was ${Hb[i.outcome]??i.outcome}. This is an automated notification: the work did NOT land \u2014 check whether it still needs to ship another way. If not, say so briefly.`:`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} has merged. This is an automated merge notification: pick up whatever follow-up work this merge unblocks (dependent tasks, cleanup, next steps that were waiting on it). If nothing needs doing, say so briefly.`}let n=t.filter(i=>i.outcome==="merged"),r=t.filter(i=>i.outcome!=="merged"),o=r.length?`${n.length} of the ${t.length} PRs on this Track merged; ${r.length} did not land:`:`All ${t.length} PRs on this Track have merged:`,s=t.map(i=>`- ${e(i)} \u2014 ${Hb[i.outcome]??i.outcome}`).join(`
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
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
386
  ${s}
387
- ${a}`}async function Fd(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||mr.has(e.state)||await tt(e.name))return!1;let n=Kb(e.agent,e.sessionId),r=await wa(n);if(r===Tr)return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===Ar&&console.warn(`[track] ${e.name}: could not confirm "${e.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await qn(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=Ao(o.hookToken),Aa({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",dt(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(o){return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(o?.message||o)}),!1}}async function vk(t){let e=await H(),n=Z(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,Ni.delete(t),dt(n,"unarchived",{}),await Fd(n),await V(),tn(n)}async function Sk(){let t=await H(),e=new Set((await Ce()).map(s=>s.name)),n=new Set((t.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of t.tracks||[])if(s.archivedAt)for(let a of s.boxes)e.has(a.name)&&!n.has(a.name)&&(await we(a.name).catch(()=>{}),dt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!mr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await V(),r}async function Tk(t){let e=await H(),n=Z(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(mr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await tt(n.leadBox)){let a=await mt(n.leadBox,{lines:_t}).catch(()=>"");if(Sa(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await we(n.leadBox),r.state="stopped"}let s=await Fd(n);if(await V(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return tn(n)}var Ak=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:Ao(t.hookToken),createdAt:Date.now()});function _k(t){return(!Array.isArray(t.epochs)||t.epochs.length===0)&&(t.epochs=[{agent:t.agent??null,sessionId:t.sessionId??null,pinned:t.pinned===!0,startedAt:t.createdAt??null,endedAt:null}]),t.epochs}var $s=new Map,Ns=t=>$s.get(t)||($e?Z($e,t)?.handoff:null)||null;async function Pi(t,e){let n=await H(),r=Z(n,t);r&&(r.handoff={...r.handoff||{},...e},await V())}function nM(t,e=Date.now()){let n=t?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=t.boxes?.find?.(i=>i.name===t.leadBox)||null;return(o?t.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&t.leadBox===n.toBox)?{...n,step:"done",finishedAt:e,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${r}"; lead "${t.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function Ek(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=nM(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await tt(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),f=_k(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:Li,at:u},dt(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await q({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await q({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await we(i).catch(()=>{}),a&&await we(a).catch(()=>{})}return e&&await V(),e}async function Ud(t,e,n=!0,r=null){let o=$s.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Z(await H(),t);if(!s)throw new Error(`track "${t}" not found`);Ui(t,"handoff");let a=await ji(e,n),i=Zr(e);if(i){let u=new Error(`can't hand off to ${e} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await tt(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:Gb(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return $s.set(t,l),await Pi(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),rM(l,s,c,a,e).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${l.step}": ${l.error}`),await Pi(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),q({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),q({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function rM(t,e,n,r,o){let s=async(R,z={})=>{t.step=R,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${R}`),await Pi(t.trackId,{step:R,toBox:t.toBox}),await q({flow:"lead-handoff",phase:R,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:z.sessionName,...z})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await H(),i=Z(a,t.trackId)?.boxes.find(R=>R.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:t.fromBox}),t.checkpoint="llm",Zr(c))t.checkpoint="skipped-capped";else try{await ah(t.fromBox,{timeoutMs:iO})}catch(R){t.checkpoint="reconstructed",await q({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:t.trackId,fromBox:t.fromBox,error:R?.message||String(R)}).catch(()=>{})}t.checkpoint!=="llm"&&(t.statusArtifact=await ih(t.trackId,n.cwd,{trackName:e.name||e.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:t.fromBox,checkpoint:t.checkpoint,statusArtifact:t.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await yu(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await pn({cwd:n.cwd,sessionId:d.sessionId,sessionName:t.fromBox,agent:o,agentCmd:Cs(o)});let p=await mt(t.fromBox,{lines:_t}).catch(()=>"");if(!Ta(o,p)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let R=Kb(c,l,{skipPermissions:t.skipPermissions});await yu(t.fromBox,{agentCmd:R,agent:c,resumeSessionId:l,pinned:u});let z=await H(),O=Z(z,t.trackId);if(O){let M=O.boxes.find(S=>S.name===t.fromBox);M&&(M.state="live"),dt(O,"lead-handoff-failed",{box:t.fromBox,fromAgent:c,toAgent:o,reason:t.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:t.startedAt}),await V()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let m=await H(),h=Z(m,t.trackId);if(!h)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=h.boxes.find(R=>R.name===t.fromBox);if(!y)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let k=Date.now(),v=_k(y),I=v[v.length-1];I.endedAt=k,t.reason&&(I.endReason=t.reason),v.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:k,endedAt:null}),y.agent=o,y.sessionId=d.sessionId,y.pinned=d.pinned,y.state="live",y.role="lead",h.pendingHandoffHydrate={artifact:t.statusArtifact||Li,at:k},dt(h,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:v.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt,checkpoint:t.checkpoint}),await V(),await s("done",{sessionName:t.fromBox}),await Pi(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var oM=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,sM=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,fo=U.join(Qe,"worktrees"),ao=[_s,Ci],le=new Map;var ho=t=>{let e=new Map([...le.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=$e?Z($e,t):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))e.has(r)||e.set(r,o);return[...e.values()].sort((r,o)=>r.startedAt-o.startedAt)},Hd=t=>({id:t.id,trackId:t.trackId,trackName:t.trackName,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,reason:t.reason||null,step:t.step,failedStep:t.failedStep||null,box:t.box,branch:t.branch,base:t.base,repos:t.repos||null,dir:t.dir,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt});async function aM(t,e,n){let r=await H(),o=Z(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await V())}var po=t=>aM(t.trackId,t.id,Hd(t));async function io(t,e){t.step=e,await po(t)}function iM(t,e=Date.now()){if(!t||t.finishedAt)return null;let n=t.step;return["snapshot","spawn","working"].includes(n)?{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; the lead's working tree may hold a PARTIAL merge \u2014 review git status. The subtask's work is preserved on branch ${t.branch}`,recovered:"partial-merge"}}function cM(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function lM(t,e,n){if(!cM(n)||le.has(e)||!await tt(n.box).catch(()=>null))return!1;try{await F.access(n.dir)}catch{return!1}let o=await tt(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return le.set(e,s),Mk(s,o).catch(a=>$k(s,a)),!0}async function Ik(){let t=await H(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(le.has(r))continue;if(await lM(n,r,o)){e=!0,dt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await q({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=iM(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!mr.has(i.state)&&(i.state="canceled"),dt(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await q({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await Wb(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||mr.has(r.state)||le.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?U.join(fo,`${n.id}-${r.subtaskId}`):null;await q({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await Wb(n,r.name,o)}}return e=await uM(t)||e,e&&await V(),e}async function Wb(t,e,n){if(e&&await we(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of qt(t.workspace)){let s=U.join(n,o.name);await ne(o.base,s).then(()=>!0,()=>!1)||await ne(o.dir,s).catch(()=>{})}await F.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await tt(t.leadBox).catch(()=>null);r?.cwd&&await ne(r.cwd,n).catch(()=>{})}async function uM(t){let e;try{e=await F.readdir(fo)}catch{return!1}let n=new Set;for(let o of t.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of le.values())o.dir&&n.add(U.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=U.join(fo,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of qt(a.workspace))await ne(i.base,U.join(s,i.name)).catch(()=>{});await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await tt(a.leadBox).catch(()=>null):null;i?.cwd?await ne(i.cwd,s).catch(()=>{}):await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await q({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Wd=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Os=class extends Error{};async function Rk(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!zr(n))throw new Error("task (string) required");let s=await ji(e,r),a=Zr(e);if(a){let p=new Error(`can't delegate to ${e} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=Z(await H(),t);if(!i)throw new Error(`track "${t}" not found`);let c=await tt(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=qt(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of p)if(!m?.dir||!await li(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);u=p.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await li(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Ms().slice(0,8),d={id:f,trackId:t,trackName:i.name,agent:e,task:zr(n),skipPermissions:r,reason:Gb(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Wd(i.name)}-${f}`,base:null,repos:u,dir:U.join(fo,`${t}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return le.set(f,d),await po(d),dM(d,c,s).catch(p=>$k(d,p)),d}async function $k(t,e){t.failedStep=t.step,t.error=e instanceof Os?"canceled":e?.message||String(e),t.step=e instanceof Os?"canceled":"error",t.finishedAt=Date.now(),await Kd(t).catch(()=>{}),await po(t).catch(()=>{}),await hM(t).catch(()=>{})}function qd(t){let e=le.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");return e.canceled=!0,e}function Ok(t){let e=le.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");if(e.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${e.step})`);return e.forceMerge=!0,e}async function dM(t,e,n){if(await io(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await ms(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await po(t)}else t.base=await ms(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await io(t,"spawn"),await F.mkdir(fo,{recursive:!0}),t.repos){await F.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Jr(a.leadDir,U.join(t.dir,a.name),t.branch,a.base);let s=qt((Z(await H(),t.trackId)||{}).workspace);await Hi(t.dir,t.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),t.branch)}else await Jr(e.cwd,t.dir,t.branch,t.base),await Md(t.dir);await fM(t,e);let r=t.trackName.slice(0,38).trim(),o=await qn(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await pM(t,o),await Vb(t.trackId,o.name,uO(t.task,!!t.repos),{kind:"subtask"}),await Mk(t,e)}async function Mk(t,e){await io(t,"working");let n=U.join(t.dir,Ci);if(await gM(n,t),t.canceled)throw new Os;if(await io(t,"merge"),t.repos){let o={files:0,insertions:0,deletions:0},s=[],a=[],i=!0;for(let c of t.repos){let l=U.join(t.dir,c.name);await Du(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await Fu(c.leadDir,c.base,t.branch,ao),c.files=await Uu(c.leadDir,c.base,t.branch,ao);let u=await Hu(c.leadDir,c.base,t.branch,ao);c.applied=u.applied,c.conflicts=u.conflicts??[],o.files+=c.stat.files,o.insertions+=c.stat.insertions,o.deletions+=c.stat.deletions,s.push(...c.files.map(f=>U.join(c.name,f))),a.push(...c.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${c.name}: ${f}`:U.join(c.name,f))),u.applied||(i=!1)}t.stat=o,t.files=s,t.conflicts=a,t.applied=i}else{await Du(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Fu(e.cwd,t.base,t.branch,ao),t.files=await Uu(e.cwd,t.base,t.branch,ao);let o=await Hu(e.cwd,t.base,t.branch,ao);t.applied=o.applied,t.conflicts=o.conflicts}await po(t);let r=await wM(t,e,n);await io(t,"resume"),await Vb(t.trackId,t.leadBox,dO(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await io(t,"reap"),await Kd(t),t.step="done",t.finishedAt=Date.now(),await mM(t),await po(t)}async function fM(t,e){let n=U.join(t.dir,U.dirname(_s));await F.mkdir(n,{recursive:!0});let r="";try{r=await Pp(e.cwd,e.sessionId)}catch{}if(!r)try{r=await F.readFile(U.join(e.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${t.id}
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
388
 
389
389
  **Delegated task:** ${t.task}
390
390
 
@@ -395,50 +395,50 @@ ${a}`}async function Fd(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name
395
395
  ## Conversation so far (lead \u2192 you)
396
396
 
397
397
  ${r}
398
- `;await F.writeFile(U.join(t.dir,_s),o)}async function pM(t,e){let n=await H(),r=Z(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:Ao(e.hookToken),createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),dt(r,"subtask-delegated",{to:e.name,toAgent:t.agent,task:t.task,subtaskId:t.id,skipPermissions:t.skipPermissions,reason:t.reason}),r.subtasks=r.subtasks||{},r.subtasks[t.id]={...r.subtasks[t.id]||{},...Hd(t)},await V())}async function mM(t){let e=await H(),n=Z(e,t.trackId);if(!n)return;let r=n.boxes.find(o=>o.subtaskId===t.id);r&&(r.state=t.conflicts.length?"conflict":"merged",r.stat=t.stat),dt(n,"subtask-merged",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,stat:t.stat,conflicts:t.conflicts.length,files:t.files,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),await V()}async function hM(t){let e=await H(),n=Z(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),dt(n,r?"subtask-canceled":"subtask-failed",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,failedStep:t.failedStep||t.step,error:t.error,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[t.id]={...n.subtasks[t.id]||{},...Hd(t)},await V()}async function wM(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=U.join(e.cwd,r);await F.mkdir(U.dirname(o),{recursive:!0}),await F.copyFile(n,o)}catch{}return r}async function Kd(t){if(t.box&&await we(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await ne(e.leadDir,U.join(t.dir,e.name)).catch(()=>{});await F.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await tt(t.leadBox).catch(()=>null))?.cwd;n&&await ne(n,t.dir).catch(()=>{})}}async function gM(t,e){let n=(e.startedAt||Date.now())+oM;for(;;){if(e.canceled||e.forceMerge)return;try{await F.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Mn(sM)}}function Mn(t){return new Promise(e=>setTimeout(e,t))}import Ps from"node:fs/promises";import Ki from"node:path";import vM from"node:os";import yM from"ws";var Ck=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Wi(t,e,n={}){let r=await fetch(`${Ck()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(n)}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}var Gd=(t,e)=>Wi("chat.postMessage",t,e),wo=(t,e)=>Wi("chat.update",t,e);async function Kt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function bM(t,e,n={}){let r=await fetch(`${Ck()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/x-www-form-urlencoded; charset=utf-8"},body:new URLSearchParams(n).toString()}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}async function Nk(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await bM("files.getUploadURLExternal",t,{filename:e,length:String(n.length)}),l=await fetch(i,{method:"POST",body:n});if(!l.ok)throw new Error(`slack upload: http ${l.status}`);return Wi("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var kM=2200;function Pk({getToken:t,onEnvelope:e,log:n=()=>{}}){let r=!1,o=null,s=null,a=1e3;async function i(){if(r)return;let l;try{let u=await t();if(!u)throw new Error("app token missing");({url:l}=await Wi("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new yM(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=h=>{try{o.send(JSON.stringify(h?{envelope_id:f.envelope_id,payload:h}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},kM);Promise.resolve().then(()=>e(f)).then(h=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?h:void 0))},h=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${h?.message||h}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var jk=()=>process.env.BOSTRAT_STATE_DIR||Ki.join(process.env.HOME||vM.tmpdir(),".bostrat"),Yd=()=>Ki.join(jk(),"slack.json"),qi=null,Vd=null,zd=Promise.resolve();async function Qd(){return qi||(Vd??=Ps.readFile(Yd(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>qi=t).finally(()=>Vd=null),Vd)}async function Dk(){let t=JSON.stringify({threads:qi},null,2),e=`${Yd()}.${process.pid}.tmp`;await Ps.mkdir(jk(),{recursive:!0}),await Ps.writeFile(e,t),await Ps.rename(e,Yd())}async function en(){zd=zd.then(Dk,Dk),await zd.catch(()=>{})}async function tf(t){return(await Qd())[t]??null}async function SM(t,e){let n=await Qd();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var Y=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function Ds(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function TM(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Ds(n.replace(/\n+/g," "),e)}function Lk(t){let e=`*${Y(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${Y(t.repoLabel)} \xB7 \`${Y(t.branch||"?")}\` \xB7 lead *${Y(t.lead)}*`,r=t.kickoff?`> ${Y(Ds(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
399
- ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Ya(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Zd(t,e){await wo(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:Lk(e)})}async function wr(t,e,n,r=void 0){return Gd(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var Bk=new Set;function hr(t){Bk.add(t),t.catch(()=>{}).finally(()=>Bk.delete(t))}var pt=t=>({response_type:"ephemeral",text:t}),AM="*/bostrat* \u2014 `start <name> [repo]` \xB7 `archive <track>` \xB7 `handoff <track> <agent>` \xB7 `stop <track>` \xB7 `list`\n_Reply in a Track's thread to steer its lead; question prompts and ship offers appear there as buttons._",_M=["claude","cursor","codex"];function Jd(t,e){let n=(e||"").toLowerCase(),r=t.filter(s=>!s.archivedAt),o=r.filter(s=>s.name.toLowerCase().startsWith(n));return o.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),{match:o[0]??null,live:r}}var Xd=(t,e)=>pt(`No live Track matches \u201C${Y(e)}\u201D. Live: ${t.map(n=>`\`${Y(n.name)}\``).join(" \xB7 ")||"none"}`);async function Fk(t,e,{name:n,repo:r,responseUrl:o}){let s=t.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=e.maxConcurrent){await Kt(o,{...pt(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Kt(o,{...pt("No designated channel configured \u2014 set `slack.channel` in integrations.json."),replace_original:!0});return}let i=await s.createTrack({name:n,...r?{repo:r}:{}}),c=i.workspace||{},l={trackId:i.id,channel:e.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?Ki.basename(c.base):"fresh workspace"),lead:"claude"},u=await Gd(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:Lk(l),unfurl_links:!1});l.ts=u.ts,(await Qd())[i.id]=l,await en(),await Kt(o,{...pt(`\u2713 *${Y(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Kt(o,{...pt(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function EM(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await tf(n.id);s&&(s.archivedAt=Date.now(),await en(),await Zd(e,s).catch(()=>{})),await Kt(r,{...pt(`\u2713 *${Y(n.name)}* archived.`),replace_original:!0})}catch(s){await Kt(r,{...pt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function IM(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Kt(o,{...pt(`\u2713 Handing *${Y(n.name)}* to ${Y(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Kt(o,{...pt(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function RM(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await Kt(r,{...pt(`\u2713 Stopped *${Y(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await Kt(r,{...pt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function $M(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Ds(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:t}),text:{type:"mrkdwn",text:`Repo for *${Y(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function OM(t){let e=await t.listTracks(),n=e.filter(s=>!s.archivedAt);if(n.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),!n.length)return"No live Tracks.";let r=n.map(s=>{let a=s.workspace||{},i=a.repo||(a.base?Ki.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${Y(s.name)}* \u2014 ${Y(i)}${a.branch?` \`${Y(a.branch)}\``:""} \xB7 lead ${Y(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
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(`
400
400
  `)+(o?`
401
- _${o} archived_`:"")}async function MM(t,e){let n=await tr();if(!n)return pt("Slack integration not configured.");let r=t.deps,o=(e.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return pt("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(f=>f.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(hr(Fk(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),pt(`Starting *${Y(u)}* on ${Y(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:$M(u,i)}}case"archive":{if(!a.length)return pt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Jd(await r.listTracks(),a.join(" "));return i?(hr(EM(t,n,{track:i,responseUrl:e.response_url})),pt(`Archiving *${Y(i.name)}*\u2026`)):Xd(c,a.join(" "))}case"handoff":{let i=r.knownAgents??_M,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return pt(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=Jd(await r.listTracks(),l);return u?(hr(IM(t,n,{track:u,agent:c,responseUrl:e.response_url})),pt(`Handing *${Y(u.name)}* to ${Y(c)}\u2026`)):Xd(f,l)}case"stop":{if(!a.length)return pt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Jd(await r.listTracks(),a.join(" "));return i?(hr(RM(t,n,{track:i,responseUrl:e.response_url})),pt(`Stopping *${Y(i.name)}*\u2026`)):Xd(c,a.join(" "))}case"list":return pt(await OM(r));default:return pt(AM)}}async function CM(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await tf(o);try{if(await t.deps.answerPrompt(o,{promptId:s,answers:[a]}),i?.prompt?.promptId===s){let{ts:c,question:l}=i.prompt;delete i.prompt,await en(),await wo(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(l||"question")}~
402
- \u2713 *${Y(a)}*`}}]}).catch(()=>{})}}catch(c){await Kt(n.response_url,pt(`Couldn't answer: ${c?.message||c}`))}}async function NM(t,e,n,r){let{trackId:o,offerId:s,action:a}=r,i=t.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(d=>d.type==="pr.offered"&&d.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===s))throw new Error("offer already answered");let f=a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;f&&await i.sayToLead(c.id,gi(f,{branch:u.payload.branch,base:u.payload.base}),{kind:f.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:f?a:"dismiss",actions:f}})}catch(c){await Kt(n.response_url,pt(`Couldn't act on the offer: ${c?.message||c}`))}}async function PM(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await tr();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await Kt(e.response_url,{...pt(`Starting *${Y(s)}*\u2026`),replace_original:!0}),hr(Fk(t,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:e.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?hr(CM(t,r,e,o)):n.action_id==="bostrat-ship"&&hr(NM(t,r,e,o))}async function DM(t,e){if(!e||e.type!=="message"||e.bot_id||e.subtype||!e.thread_ts||e.thread_ts===e.ts||!e.text?.trim())return;let n=await SM(e.channel,e.thread_ts);if(!n)return;let r=await tr();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await wr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function BM(t){return async e=>{if(e.type==="slash_commands")return MM(t,e.payload);if(e.type==="interactive"){await PM(t,e.payload);return}if(e.type==="events_api"){await DM(t,e.payload?.event);return}}}var jM=new Set(["turn.user","turn.agent","prompt.raised","prompt.answered","pr.offered","pr.offer.answered","pr.state.changed","handoff.done","view.state.changed","mock.state.changed"]);function LM(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${Y(e.question||"The lead needs an answer")}`}}],r=(e.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:Ds(String(o),75)},value:JSON.stringify({trackId:t,promptId:e.promptId,label:String(o)})}))}):n.push({type:"context",elements:[{type:"mrkdwn",text:"_reply in this thread to answer_"}]}),n}async function FM(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await wo(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await wr(t,e,r.question||"The lead needs an answer",LM(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await en()}async function UM(t,e,n){let r=n.payload||{};if(!e.prompt||r.promptId&&e.prompt.promptId!==r.promptId)return;let{ts:o,question:s}=e.prompt;delete e.prompt,await en(),await wo(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(s||"question")}~
403
- \u2713 _answered_`}}]})}async function HM(t,e,n){let r=n.payload||{};if(!r.offerId||e.offers?.[r.offerId])return;let o=(i,c,l)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i}),...l?{style:l}:{}}),s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")],a=await wr(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Y(Ds(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await en()}async function WM(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await en();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await wo(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function qM(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??KM)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Nk(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${Y(o.name)}*${a}${o.group?` (group \`${Y(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${Y(a.name)}*`).join(", ");await wr(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${Y(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function KM(t,e){let n=await Be(t);if(!n)return null;let r=ae(n,e);if(!r)return null;let o=await Ps.readFile(ee(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function GM(t,e){if(!jM.has(e.type))return;let n=await tf(e.trackId);if(!n)return;let r=await tr();if(!r)return;let o=e.payload||{};switch(e.type){case"turn.user":if(n.kickoff||e.actor!=="user"||!o.text)return;n.kickoff=o.text,await en(),await Zd(r,n);return;case"turn.agent":{if(!o.text)return;let s=TM(o.text);if(!s)return;await wr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Y(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Ya(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return FM(r,n,e);case"prompt.answered":return UM(r,n,e);case"pr.offered":return HM(r,n,e);case"pr.offer.answered":return WM(r,n,e);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await wr(r,n,`${s}${o.url?`: ${o.url}`:o.number?` (#${o.number})`:""}`);return}case"handoff.done":if(!o.toAgent||o.toAgent===n.lead)return;n.lead=o.toAgent,await en(),await Zd(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await wr(r,n,`Live preview ${o.name}: ${Qa(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Y(o.name)}*: ${Qa(o.slug)}
404
- _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return qM(r,n,e,t.deps)}}function Uk(t){let e={deps:t};Ht.on("event",s=>{GM(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await tr()||(r&&clearInterval(r),r=null,n=Pk({getToken:async()=>(await tr())?.appToken,onEnvelope:BM(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import VM from"node:fs/promises";function zM(t){let e=new Map;for(let n of String(t||"").split(`
405
- `)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&e.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...e.values()]}function JM(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}var XM=new Set(["Write","Edit","NotebookEdit"]),YM=220,ZM=160;function QM(t,e){let n=t?.tool_name??null,r={tool:n},o=t?.tool_input;if(!o||typeof o!="object")return r;if(XM.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=e?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,YM)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Hn(o.command.trim().replace(/\s+/g," ")).slice(0,ZM));return r}function tC(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Ne(e.cwd,e.sessionId):e?.cwd?zc(e.cwd):null}async function Hk(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await $(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":{let c=String(t.prompt??t.user_prompt??""),l=Si(c);await i({type:"turn.user",actor:l?"peer":"user",payload:{promptHead:(l?l.text:c).slice(0,160)||null,...l?{peer:{socket:l.socket,name:l.name,mode:l.mode}}:{}}});break}case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:QM(t,o)});break;case"Stop":await eC(r,o,t,i);break;default:break}return a}async function eC(t,e,n,r){let o=[],s=tC(n,e);if(s)try{o=zM(await VM.readFile(s,"utf8"))}catch{}let a=o.length?o[o.length-1]:null;if(await r({type:"turn.agent",actor:"agent",payload:{model:a?.model??e?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=JM(await B(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=f.model||"unknown",p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=f.tokens[m];p.messageIds.push(f.messageId),u.set(d,p)}for(let[f,d]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:gn(f,d.tokens),tokens:d.tokens,messageId:d.messageIds[d.messageIds.length-1],messageIds:d.messageIds,apiCalls:d.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}import oC from"node:fs";import ef from"node:fs/promises";import sC from"node:http";import go from"node:path";import zi from"node:os";import{createRequire as aC}from"node:module";import nf from"ws";import nC from"node:path";import rC from"node:os";var Bs=process.env.BOSTRAT_STATE_DIR||nC.join(process.env.HOME||rC.tmpdir(),".bostrat");function Wk(t=process.env){let e=(t.BOSTRAT_HOST||"").trim();if(e!=="all")return e||(t.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function qk(t){let e=String(t||"").trim();if(!e)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`wss://${e}`,r;try{r=new URL(n)}catch{return e}let o={"https:":"wss:","http:":"ws:","wss:":"wss:","ws:":"ws:"}[r.protocol];return o?(r.protocol=o,(r.pathname===""||r.pathname==="/")&&(r.pathname="/v1/node"),r.toString()):e}var Gi=(()=>{try{return aC(import.meta.url)("../package.json").version||null}catch{return null}})(),iC=process.env.BOSTRAT_STATE_DIR||go.join(process.env.HOME||zi.tmpdir(),".bostrat");function cC(t,e){let n=String(t).split(".").map(Number),r=String(e).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function lC({home:t=process.env.HOME||zi.homedir(),env:e=process.env}={}){let n=r=>{try{return oC.statSync(r),!0}catch{return!1}};return{claude:n(go.join(t,".claude",".credentials.json")),codex:n(go.join(e.CODEX_HOME||go.join(t,".codex"),"auth.json"))}}var Rt=1,Kk=1e3,uC=3e4,dC=6e4,fC=1500,pC=500,Vi=100,mC=2e3,hC=25e3,wC=6e4,gC=6e4;function Gk({url:t,token:e,nodeId:n=zi.hostname(),name:r=zi.hostname(),listTracks:o,detectAgents:s=null,sampleMetrics:a=null,metricsMs:i=gC,selfUrl:c=`http://127.0.0.1:${process.env.PORT||3362}`,log:l=console}){if(!t||!e)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");t=qk(t);let u=c.replace(/^http/,"ws"),f=new Map,d=new Map,p=go.join(iC,"plane-link","cursor.json"),m={},h={},y=new Map,k=null,v=!1,I=!1,R=Kk,z=null,O=null,M=null,S=null,N=null,D=!1;async function it(){try{m=JSON.parse(await ef.readFile(p,"utf8"))}catch{m={}}}function ct(){D=!0,!M&&(M=setTimeout(async()=>{if(M=null,!!D){D=!1;try{await ef.mkdir(go.dirname(p),{recursive:!0}),await ef.writeFile(p,JSON.stringify(m))}catch(G){l.error("[plane-link] cursor flush failed:",G?.message||G)}}},mC),M.unref?.())}function T(G){return!k||k.readyState!==nf.OPEN?!1:(k.send(JSON.stringify(G)),!0)}function g(G){let j=Date.now();for(let{trackId:b,event:_}of G){let lt=y.get(_.id);y.set(_.id,{trackId:b,seq:_.seq,event:_,sentAt:j,tries:(lt?.tries??0)+1})}return T({v:Rt,ch:"events",type:"append",events:G})}function w(G){let j=null;for(let b of y.values())b.trackId===G&&(j==null||b.seq<j)&&(j=b.seq);return j}function A(G){let j=h[G];if(j==null)return;let b=w(G),_=b==null?j:Math.min(j,b-1);_>(m[G]??0)&&(m[G]=_,ct())}function E(){if(!v)return;let G=Date.now(),j=[...y.values()].filter(b=>b.event&&G-b.sentAt>=wC);if(j.length){j.sort((b,_)=>b.trackId===_.trackId?b.seq-_.seq:b.trackId<_.trackId?-1:1),l.log(`[plane-link] re-sending ${j.length} unacked event(s) the plane never confirmed`);for(let b=0;b<j.length;b+=Vi){let _=j.slice(b,b+Vi).map(lt=>({trackId:lt.trackId,event:lt.event}));if(!g(_))return}}}async function J(){if(!a)return;let G;try{G=await a()}catch(j){l.error("[plane-link] metrics sample failed:",j?.message||j);return}G&&T({v:Rt,ch:"ctl",type:"metrics",metrics:G})}function W(){a&&(N&&clearInterval(N),J(),N=setInterval(J,i),N.unref?.())}async function at(){let G;try{G=await o()}catch(j){l.error("[plane-link] backfill listTracks failed:",j?.message||j);return}T({v:Rt,ch:"ctl",type:"tracks",ids:G.map(j=>j.id).filter(j=>typeof j=="string")});for(let j of G){if(!v)return;let b;try{b=await B(j)}catch{continue}let _=m[j.id]??0,lt=b.filter(nt=>nt.seq>_&&typeof nt.id=="string");for(let nt=0;nt<lt.length;nt+=Vi){let bt=lt.slice(nt,nt+Vi).map(jt=>({trackId:j.id,event:jt}));if(!g(bt))return}}}function Nt(G){!v||!G?.trackId||typeof G.id!="string"||g([{trackId:G.trackId,event:G}])}let Tt=new Map;async function Xs(G){let{reqId:j,method:b,path:_,headers:lt={},body:nt}=G,bt=Object.fromEntries(Object.entries(lt).filter(([,pe])=>pe!=null)),jt=new AbortController;Tt.set(j,jt);try{if(G.host){await gr(G,bt,jt);return}let pe=await fetch(c+_,{method:b,headers:bt,body:nt?Buffer.from(nt,"base64"):void 0,signal:jt.signal}),rn=Object.fromEntries(pe.headers);for(let Zt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete rn[Zt];if(rn["content-type"]||(rn["content-type"]="application/octet-stream"),T({v:Rt,ch:"control",type:"http_head",reqId:j,status:pe.status,headers:rn}),pe.body){for await(let Zt of pe.body)if(!T({v:Rt,ch:"control",type:"http_data",reqId:j,data:Buffer.from(Zt).toString("base64")}))break}T({v:Rt,ch:"control",type:"http_end",reqId:j,error:null})}finally{Tt.delete(j)}}function gr(G,j,b){let{reqId:_,method:lt,path:nt,body:bt}=G,jt=new URL(c);return new Promise(pe=>{let rn=sC.request({host:jt.hostname,port:jt.port,method:lt,path:nt,headers:{...j,host:G.host},signal:b.signal},Zt=>{let{connection:lD,"keep-alive":uD,"transfer-encoding":dD,upgrade:fD,...Mx}=Zt.headers;T({v:Rt,ch:"control",type:"http_head",reqId:_,status:Zt.statusCode||502,headers:Mx}),Zt.on("data",Cx=>{T({v:Rt,ch:"control",type:"http_data",reqId:_,data:Buffer.from(Cx).toString("base64")})||Zt.destroy()}),Zt.on("end",()=>{T({v:Rt,ch:"control",type:"http_end",reqId:_,error:null}),pe()}),Zt.on("error",()=>{T({v:Rt,ch:"control",type:"http_end",reqId:_,error:"view upstream error"}),pe()})});rn.on("error",Zt=>{T({v:Rt,ch:"control",type:"http_end",reqId:_,error:Zt?.message||"view request failed"}),pe()}),bt&&rn.write(Buffer.from(bt,"base64")),rn.end()})}function Ys(){if(I)return;k=new nf(t,{headers:{authorization:`Bearer ${e}`}}),k.on("open",async()=>{let j=null;try{j=await s?.()||null}catch{j=null}T({v:Rt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Gi,providers:lC(),...j?{agents:j}:{}})}),k.on("message",j=>{let b;try{b=JSON.parse(j)}catch{return}if(b.ch==="ctl"&&b.type==="hello_ok"){v=!0,R=Kk,z=null,l.log(`[plane-link] connected to plane as ${n}`),b.minNodeVersion&&Gi&&cC(Gi,b.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${Gi}; the plane asks for \u2265${b.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),S&&clearInterval(S),S=setInterval(()=>{T({v:Rt,ch:"ctl",type:"ping"}),E()},hC),S.unref?.(),W(),at().catch(_=>l.error("[plane-link] backfill failed:",_?.message||_));return}if(b.ch==="ctl"&&b.type==="usage_caps"){pi(b.caps??{},{source:"plane",updatedAt:b.updatedAt??Date.now()}).then(_=>l.log(`[plane-link] spend caps ${Object.keys(_.caps).length?`updated (${Object.keys(_.caps).join(", ")})`:"cleared"} from plane`)).catch(_=>l.error("[plane-link] spend-caps push rejected:",_?.message||_));return}if(b.ch==="events"&&b.type==="ack"&&Array.isArray(b.ids)){let _=new Set;for(let lt of b.ids){let nt=y.get(lt);nt&&(y.delete(lt),(h[nt.trackId]??0)<nt.seq&&(h[nt.trackId]=nt.seq),_.add(nt.trackId))}for(let lt of _)A(lt);return}if(b.ch==="control"&&b.type==="http_open"){Xs(b).catch(_=>T({v:Rt,ch:"control",type:"http_end",reqId:b.reqId,error:_?.message||"tunnel error"}));return}if(b.ch==="control"&&b.type==="http_cancel"){Tt.get(b.reqId)?.abort();return}if(b.ch==="attach"&&b.type==="open"){let _=new nf(u+b.path,Array.isArray(b.protocols)?b.protocols:[],b.host?{headers:{host:b.host}}:void 0);f.set(b.streamId,_),d.set(b.streamId,[]),_.on("open",()=>{let nt=d.get(b.streamId)||[];d.delete(b.streamId);for(let{buf:bt,bin:jt}of nt)_.send(bt,{binary:jt})}),_.on("message",(nt,bt)=>{let jt=bt?nt:Buffer.from(String(nt));T({v:Rt,ch:"attach",type:"data",streamId:b.streamId,data:jt.toString("base64"),...bt?{bin:!0}:{}})});let lt=()=>{d.delete(b.streamId),f.delete(b.streamId)&&T({v:Rt,ch:"attach",type:"close",streamId:b.streamId})};_.on("close",lt),_.on("error",lt);return}if(b.ch==="attach"&&b.type==="data"){let _=f.get(b.streamId);if(!_)return;let lt=Buffer.from(b.data,"base64"),nt=b.bin===!0,bt=d.get(b.streamId);bt?bt.push({buf:lt,bin:nt}):_.send(lt,{binary:nt});return}if(b.ch==="attach"&&b.type==="close"){let _=f.get(b.streamId);f.delete(b.streamId),d.delete(b.streamId),_?.close();return}});let G=()=>{v&&l.log("[plane-link] disconnected from plane"),v=!1,S&&(clearInterval(S),S=null),N&&(clearInterval(N),N=null),y.clear(),h={};for(let _ of f.values())_.close();f.clear(),d.clear();for(let _ of Tt.values())_.abort();if(Tt.clear(),I||O)return;z==null&&(z=Date.now());let j=Date.now()-z<dC,b=j?fC+Math.random()*pC:R;O=setTimeout(()=>{O=null,Ys()},b),O.unref?.(),j||(R=Math.min(R*2,uC))};k.on("close",G),k.on("error",G)}return Ht.on("event",Nt),it().then(Ys),{stop:()=>{I=!0,Ht.off("event",Nt),O&&clearTimeout(O),M&&clearTimeout(M),S&&clearInterval(S),N&&clearInterval(N),T({v:Rt,ch:"ctl",type:"bye",reason:"shutdown"}),k?.close()},status:()=>({connected:v,nodeId:n,cursor:{...m},inflight:y.size})}}import yC from"node:fs";import Vk from"node:fs/promises";import js from"node:os";import bC from"node:path";var kC=1024*1024,Ji=t=>Math.round(t/kC);async function xC(t){try{let e=await Vk.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:Ji(r),diskTotalMb:Ji(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function vC(){try{return(await Ce()).filter(e=>e.isBostrat).length}catch{return null}}async function SC(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await Vk.readdir(bC.join(Bs,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function zk({stateDir:t=Bs,now:e=Date.now}={}){let n=js.totalmem(),[r,o,s,a]=await Promise.all([Ba().catch(()=>null),xC(yC.existsSync(t)?t:js.tmpdir()),vC(),SC()]),i=js.cpus()?.length||null,c=js.loadavg()?.[0]??null;return{ts:e(),loadavg1:c!=null?Math.round(c*100)/100:null,cpus:i,cpuPct:c!=null&&i?Math.min(100,Math.round(c/i*100)):null,memUsedMb:r!=null?Ji(n-r):null,memTotalMb:Ji(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(js.uptime())}}function rf(t){let e={};for(let n of t.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";e[n.name]=`${n.name} (${n.agent||"?"}${r})`}return e}var Jk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Xk=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Xi=t=>Math.round(t*1e4)/1e4;async function Yk(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=Jk(),f=0,d=[];for(let p of t||[]){let m=await B(p),h=ye(m,{boxLabels:rf(p)});if(h.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.asRecordedUsd||0,c+=h.unpricedTurns||0;for(let y of h.unpricedModels||[])l.add(y);Xk(u,h.totalTokens);for(let[y,k]of Object.entries(h.byModel))n[y]||={usd:0,events:0,tokens:Jk()},n[y].usd=Xi(n[y].usd+k.usd),n[y].events+=k.events,Xk(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(h.byProvider))r[y]||={usd:0,events:0},r[y].usd=Xi(r[y].usd+k.usd),r[y].events+=k.events;e.push({id:p.id,name:p.name,usd:h.totalUsd,rollup:h})}return e.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Xi(o),asRecordedUsd:Xi(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:e}}var Cn=t=>`$${t.toFixed(4)}`,yo=(t,e)=>String(t).padEnd(e);function Zk(t){let{fleet:e,tracks:n}=t,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${t.generatedAt}`),t.pricingBasis&&r.push(`basis: ${t.pricingBasis}`);let o=e.tracksSilentLive||[];r.push(`fleet total ${Cn(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),e.asRecordedUsd!=null&&Math.abs(e.asRecordedUsd-e.totalUsd)>=.005&&r.push(`as-recorded total ${Cn(e.asRecordedUsd)} \xB7 current-rates total ${Cn(e.totalUsd)} (${e.totalUsd>=e.asRecordedUsd?"+":""}${Cn(e.totalUsd-e.asRecordedUsd)})`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${yo(a,26)} ${yo(Cn(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${yo(a.name||a.id,30)} ${Cn(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${yo(c.label,40)} ${yo(Cn(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((f,d)=>Number(f[0])-Number(d[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${yo(Cn(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
406
- `)}import IC from"node:fs/promises";import RC from"node:path";var Yi=t=>t.payload?.ts??t.ts,TC=12e4,Nn=t=>Math.round(t*1e4)/1e4,AC=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function _C(t,{since:e}){let{track:n,events:r,brief:o}=t,s=v=>Yi(v)>=e,a=Gn([Uo(n?.name),Uo(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let v of r)if(v.type==="subtask.delegated"&&v.boxId&&v.payload?.task){let I=Gn(v.payload.task);I&&i.set(v.boxId,I)}let c=new Map,l=v=>{let I=v||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},u=v=>({textCat:v,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((v,I)=>Yi(v)-Yi(I)),d=0,p=0,m=0,h={turns:0};for(let v of f){let I=l(v.boxId),R=I.segments[I.segments.length-1],z=Yi(v);switch(I.lastTs!=null&&z>I.lastTs&&s(v)&&(R.activeMs+=Math.min(z-I.lastTs,TC)),I.lastTs=z,v.provider&&(I.provider=v.provider),v.type){case"turn.user":{let O=v.payload?.text;if(v.source==="hook"||!String(O||"").trim())break;let M=u(Gn(O));I.segments.push(M),s(v)&&(M.turns+=1);break}case"peer.message":{let O=v.payload?.text;if(!String(O||"").trim())break;let M=u(Gn(O));I.segments.push(M),s(v)&&(M.turns+=1);break}case"tool.called":{if(v.source==="transcript")break;let O=v.payload?.tool;O&&(R.tools[O]=(R.tools[O]||0)+1),s(v)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(v))break;let{usd:O,tokens:M,model:S,unpriced:N}=De(v);N&&(h.turns+=1),R.usd+=O,M&&(R.tokens.input+=M.input,R.tokens.output+=M.output,R.tokens.cacheRead+=M.cacheRead,R.tokens.cacheWrite+=M.cacheWrite),R.models[S]=(R.models[S]||0)+O,v.provider&&(R.provider=v.provider);break}case"handoff.done":s(v)&&(d+=1);break;case"subtask.merged":s(v)&&(p+=1);break;case"error":s(v)&&v.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,k=!1;for(let[v,I]of c)for(let R of I.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let O=R.textCat?.category??i.get(v)?.category??Yp(R.tools)?.category??a?.category??"uncategorized";y.has(O)||y.set(O,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let M=y.get(O);M.usd+=R.usd,M.activeMs+=R.activeMs,M.turns+=R.turns,M.toolCalls+=R.toolCalls;for(let[N,D]of Object.entries(R.models))M.models[N]=(M.models[N]||0)+D;let S=R.provider||I.provider;S&&(M.providers[S]=(M.providers[S]||0)+R.usd),S==="cursor"&&R.usd===0&&(k=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:k,unpricedTurns:h.turns}}function t0(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let h of t||[]){let{track:y,events:k}=h;if(!k?.length){a+=1;continue}let{perCategory:v,handoffs:I,merged:R,failed:z,sawCursorActivity:O,unpricedTurns:M}=_C(h,{since:r});O&&(i=!0),c+=M;let S={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:I,subtasksMerged:R,subtasksFailed:z};for(let[N,D]of v){S.usd+=D.usd,S.activeMs+=D.activeMs,S.turns+=D.turns,S.categories[N]={usd:Nn(D.usd),activeMs:D.activeMs};for(let[T,g]of Object.entries(D.models))S.models[T]=Nn((S.models[T]||0)+g);for(let[T,g]of Object.entries(D.providers))S.providers[T]=Nn((S.providers[T]||0)+g);o.has(N)||o.set(N,AC());let it=o.get(N);it.usd+=D.usd,it.activeMs+=D.activeMs,it.turns+=D.turns,it.toolCalls+=D.toolCalls;for(let[T,g]of Object.entries(D.models))it.models[T]=(it.models[T]||0)+g;for(let[T,g]of Object.entries(D.providers))it.providers[T]=(it.providers[T]||0)+g;let ct=it.byTrack.get(y?.id)||{usd:0,activeMs:0};ct.usd+=D.usd,ct.activeMs+=D.activeMs,it.byTrack.set(y?.id,ct)}(S.usd>0||S.activeMs>0||S.turns>0)&&(S.usd=Nn(S.usd),S.dominantCategory=EC(S.categories),s.push(S))}let l=[...o.values()].reduce((h,y)=>h+y.usd,0),u=[...o.values()].reduce((h,y)=>h+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((t||[]).map(h=>[h.track?.id,h.track?.name||h.track?.id])),m=[...o.entries()].map(([h,y])=>({category:h,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:Nn(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:Qk(y.models),providers:Qk(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((k,v)=>v[1].usd-k[1].usd||v[1].activeMs-k[1].activeMs).slice(0,2).map(([k,v])=>({trackId:k,name:p.get(k),usd:Nn(v.usd),activeMs:v.activeMs}))})).sort((h,y)=>y.usd-h.usd||y.activeMs-h.activeMs);return s.sort((h,y)=>y.usd-h.usd||y.activeMs-h.activeMs),{since:r,capturedAt:n,totalUsd:Nn(l),activeMs:u,turns:[...o.values()].reduce((h,y)=>h+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var EC=t=>{let e=null;for(let[n,r]of Object.entries(t))(!e||r.usd>e.v.usd||r.usd===e.v.usd&&r.activeMs>e.v.activeMs)&&(e={cat:n,v:r});return e?.cat??null},Qk=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Nn(n)]));var bo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function $C(t){let e=String(t||"7d");return e in bo?{key:e,ms:bo[e]}:{key:"7d",ms:bo["7d"]}}function OC(t,e){let n=new Set((e||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...e||[]];for(let o of t?.events||[]){let s=wi(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:t.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function MC(t){if(!t?.cwd)return null;try{let n=(await IC.readFile(RC.join(t.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function e0(t,{window:e="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:$C(e),f=[];for(let d of t||[]){let p=await B(d),m=OC(d,p);i&&c<o&&(m=m.filter(h=>(h.ts??0)<=c)),f.push({track:d,events:m,brief:await MC(d)})}return{window:l,...t0(f,{windowMs:u,now:c})}}var Pn=36e5,s0=24*Pn,CC=400,NC=12,ko=t=>Math.round(t*1e4)/1e4;function PC({window:t,from:e,to:n,now:r=Date.now()}={}){let o=Number(e),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(t||"7d")in bo?String(t||"7d"):"7d",i=bo[a];return{key:a,since:i==null?null:r-i,until:r}}var DC=(t,e)=>t!=null&&e-t<=48*Pn?Pn:s0;function n0(t,e){if(e===Pn)return Math.floor(t/Pn)*Pn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function BC(t,e){if(e===Pn)return t+Pn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var r0=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),o0=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},jC=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,ko(n)]));async function a0(t,e={}){let n=e.now??Date.now(),r=PC({...e,now:n}),o=r.since==null?s0:DC(r.since,r.until),s=S=>(r.since==null||S>=r.since)&&S<r.until,a=r.since!=null?r.until-r.since:null,i=S=>a!=null&&S>=r.since-a&&S<r.since,c={},l={},u=r0(),f=0,d=0,p=0,m=0,h=0,y=new Set,k=new Map,v=[],I=[],R=[],z=null;for(let S of t||[]){let D=(await B(S)).filter(T=>T.type==="cost.recorded");if(!D.length){S.archivedAt||R.push(S.name||S.id);continue}let it=[];for(let T of D){let g=T.ts??0;s(g)?it.push(T):i(g)&&(h+=De(T).usd)}if(!it.length)continue;let ct=ye(it,{boxLabels:rf(S)});f+=ct.totalUsd,d+=ct.turns,p+=ct.repricedTurns||0,m+=ct.unpricedTurns||0;for(let T of ct.unpricedModels||[])y.add(T);o0(u,ct.totalTokens);for(let[T,g]of Object.entries(ct.byModel))c[T]||={usd:0,events:0,tokens:r0()},c[T].usd=ko(c[T].usd+g.usd),c[T].events+=g.events,o0(c[T].tokens,g.tokens);for(let[T,g]of Object.entries(ct.byProvider))l[T]||={usd:0,events:0},l[T].usd=ko(l[T].usd+g.usd),l[T].events+=g.events;for(let T of it){let{usd:g}=De(T);if(!g)continue;let w=n0(T.ts??0,o),A=k.get(w)??{t:w,total:0,byProvider:{}};A.total+=g;let E=T.provider||"unknown";A.byProvider[E]=(A.byProvider[E]||0)+g,k.set(w,A),(z==null||w<z)&&(z=w)}v.push({id:S.id,name:S.name||S.id,archived:!!S.archivedAt,usd:ct.totalUsd,turns:ct.turns,providers:Object.keys(ct.byProvider)});for(let[T,g]of Object.entries(ct.byBox))I.push({trackId:S.id,trackName:S.name||S.id,boxId:T,...g})}let O=r.since!=null?n0(r.since,o):z,M=[];for(let S=O,N=0;S!=null&&S<r.until&&N<CC;S=BC(S,o),N++){let D=k.get(S);M.push(D?{t:S,total:ko(D.total),byProvider:jC(D.byProvider)}:{t:S,total:0,byProvider:{}})}return v.sort((S,N)=>N.usd-S.usd),I.sort((S,N)=>N.usd-S.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:M,fleet:{totalUsd:ko(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:v.length,tracksSilentLive:R},prior:a!=null?{totalUsd:ko(h),since:r.since-a,until:r.since}:null,tracks:v.slice(0,50),boxes:I.slice(0,NC)}}import xo from"node:fs/promises";import LC from"node:fs";import Ct from"node:path";import FC from"node:os";import{execFile as UC}from"node:child_process";import{promisify as HC}from"node:util";var WC=HC(UC),i0=process.env.BOSTRAT_STATE_DIR||Ct.join(process.env.HOME||FC.tmpdir(),".bostrat"),Zi=!1,of=null;async function sf(t,e){let n=await xo.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await xo.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await xo.mkdir(Ct.dirname(e),{recursive:!0}),await xo.copyFile(t,e),!0)}var qC=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function Qi(t,e,{skip:n=null}={}){let r=0,o=await xo.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(qC.has(s.name)||n?.has(s.name))continue;let a=Ct.join(t,s.name);s.isDirectory()?r+=await Qi(a,Ct.join(e,s.name)):s.isFile()&&(r+=await sf(a,Ct.join(e,s.name))?1:0)}return r}async function c0({stateDir:t=i0,dest:e=process.env.BOSTRAT_SYNC_DIR||`${i0}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await xo.mkdir(e,{recursive:!0});let o=0;o+=await sf(Ct.join(t,"tracks.json"),Ct.join(e,"tracks.json"))?1:0,o+=await sf(Ct.join(t,"events.jsonl"),Ct.join(e,"events.jsonl"))?1:0,o+=await Qi(Ct.join(t,"events"),Ct.join(e,"events")),o+=await Qi(Ct.join(t,"memory"),Ct.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Qi(Ct.join(u.cwd,".bostrat"),Ct.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>WC("git",["-C",e,...u]);LC.existsSync(Ct.join(e,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Tc(e).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||Zi))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Zi&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Zi=!1,of=null}catch(u){Zi=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
407
- `).slice(-3).join(" | ").slice(0,300);f!==of&&(of=f,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${f}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as KC}from"node:child_process";import{promisify as GC}from"node:util";var VC=GC(KC),zC=12e4,JC=6e4;function XC(t){return`refs/bostrat/wip/${t}`}function d0(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function YC(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:zC}var tc=(t,e,n=15e3)=>VC("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),l0=new Map,u0=new Set;async function ZC(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await tc(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await tc(r,["remote","get-url","origin"])}catch{return u0.has(r)||(u0.add(r),e.log?.(`[wip-sync] ${n}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let o=await ms(r,"bostrat: wip snapshot"),{stdout:s}=await tc(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(l0.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=XC(n);return await tc(r,["push","--force","origin",`${o}:${i}`],JC),l0.set(r,a),await $({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
408
- `)[0]}`),{pushed:!1,reason:"push-failed"}}}function f0({listTracks:t,sweepMs:e=YC(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await t()??[];for(let i of a)i?.archivedAt||i?.kind==="setup"||await ZC(i,{log:n})}catch(a){n.log?.(`[wip-sync] sweep failed: ${a?.message||a}`)}finally{r=!1}}},s=setInterval(o,e);return s.unref?.(),o(),{stop:()=>clearInterval(s),cycle:o}}import{execFile as QC}from"node:child_process";import{promisify as tN}from"node:util";import{randomUUID as eN}from"node:crypto";import nN from"node:fs";import rN from"node:os";import oN from"node:path";var Bn=tN(QC),ec=t=>new Promise(e=>setTimeout(e,t)),jn=Uc,af=process.env.BOSTRAT_USAGE_PROBE_CWD||oN.join(rN.homedir(),".bostrat","usage-probe"),sN=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,aN=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,iN=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,p0=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Dn=null,Ls=null,Fs=null;async function cN(){try{return await Bn(C,P("has-session","-t",jn)),!0}catch{return!1}}function lN(){Dn&&clearTimeout(Dn),Dn=setTimeout(()=>uN().catch(()=>{}),sN),Dn.unref&&Dn.unref()}async function uN(){Dn&&(clearTimeout(Dn),Dn=null);try{await Bn(C,P("kill-session","-t",jn))}catch{}}async function dN(){let t=Date.now()+iN;for(;;){let e=await cf().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await ec(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await ec(500)}}async function fN(){if(await cN())return;if(!await un())throw new Error("host tmux server not running \u2014 start globo-tmux.service");nN.mkdirSync(af,{recursive:!0}),await on(af);let t=eN();await Bn(C,P("new-session","-d","-s",jn,"-c",af,"-x","200","-y","60",...Fc()));let e=`${Lc} --session-id ${t}`;await Bn(C,P("send-keys","-t",jn,e,"Enter")),await dN()}async function cf(){let{stdout:t}=await Bn(C,P("capture-pane","-t",jn,"-p"));return t}async function pN(){await fN(),await Bn(C,P("send-keys","-t",jn,"-l","/usage")),await Bn(C,P("send-keys","-t",jn,"Enter"));let t=Date.now()+aN,e="";for(;await ec(400),e=await cf(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await ec(900),e=await cf(),await Bn(C,P("send-keys","-t",jn,"Escape")).catch(()=>{}),lN(),e}function mN(t){let e=String(t||"").split(`
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
409
  `).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,e.length)&&!/^Current\s/i.test(e[c]);c++){let l=e[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=e[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=e.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return e.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function hN(t){let e=String(t||"").split(`
410
- `).map(s=>s.replace(/\s+$/,"")),n=e.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<e.length&&!r.test(e[s]);s++)e[s].trim()&&o.push(e[s].trim());return o.length?o:null}async function m0({fresh:t=!1}={}){return!t&&Ls&&Date.now()-Ls.at<p0?Ls.data:Fs||(Fs=(async()=>{try{let e=await pN(),n=mN(e),r={...n,contributors:hN(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ls={at:Date.now(),data:r},r}catch(e){let n={session:null,week:null,contributors:null,subscription:!1,error:e?.message||String(e),capturedAt:Date.now()};return Ls={at:Date.now()-(p0-3e3),data:n},n}finally{Fs=null}})(),Fs)}var wN=262144;function gN(t){let e=Number(t);if(!Number.isFinite(e)||e<0)return null;let n=Math.floor(e/3600),r=Math.round(e%3600/60);return n>=48?`in ${Math.round(n/24)}d`:n>0?`in ${n}h ${r}m`:`in ${Math.max(1,r)}m`}function h0(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:gN(t.resets_in_seconds)}:null}function yN(t){let e=String(t||"").split(`
411
- `),n=!1;for(let r=e.length-1;r>=0;r--){let o=e[r].trim();if(!o||o[0]!=="{"||!o.includes("token_count"))continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s?.payload;if(a?.type!=="token_count")continue;n=!0;let i=a.rate_limits;if(!i)return{session:null,week:null,sawTokenCount:n};if(i.primary||i.secondary)return{session:h0(i.primary),week:h0(i.secondary),sawTokenCount:n};if(i.primary_used_percent!=null||i.secondary_used_percent!=null){let c=l=>Number.isFinite(Number(l))?{pct:Math.round(Number(l)),resets:null}:null;return{session:c(i.primary_used_percent),week:c(i.secondary_used_percent),sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}async function lf(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Vn(t?.cwd,t?.sessionId);if(!n)return{...e,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await Ut(n.file,wN),o=yN(r);return{...e,session:o.session,week:o.week,subscription:!!(o.session||o.week),capturedAt:n.mtimeMs||Date.now()}}catch(n){return{...e,session:null,week:null,subscription:!1,error:n?.message||String(n),capturedAt:Date.now()}}}async function w0(t,{fresh:e=!1}={}){let n=new Set;for(let o of t||[])for(let s of o.boxes||[])s.agent&&n.add(s.agent);let r=[];if((n.has("claude")||n.size===0)&&r.push(await nc({agent:"claude"},{fresh:e})),n.has("codex")){let o=[],s=[...t||[]].sort((i,c)=>(i.archivedAt?1:0)-(c.archivedAt?1:0)||(c.createdAt||0)-(i.createdAt||0));for(let i of s)for(let c of i.boxes||[])c.agent==="codex"&&o.push({...c,cwd:c.cwd||i.cwd});let a=null;for(let i of o.slice(0,8)){let c=await lf(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await lf(null))}return n.has("cursor")&&r.push(await nc({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function nc(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?lf(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await m0({fresh:e})}}function bN(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var kN=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),rc=(t,e)=>Object.assign(new Error(t),{status:e});async function g0(t,e){if(!bN(e))throw rc("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Qt(e)}${t}`,{headers:kN(e)})}catch(o){throw rc(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw rc(r?.error||`plane replied ${n.status}`,n.status);return r}async function y0(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await g0(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function b0(t,{env:e=process.env}={}){if(!t)throw rc("message id required",400);return(await g0(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function uf(t){let e=t.split(".");if(e.length!==4)return null;let n=0;for(let r of e){let o=Number(r);if(!Number.isInteger(o)||o<0||o>255)return null;n=n*256+o}return n>>>0}function xN(t){let[e,n]=t.split("/"),r=uf(e);if(r==null)return null;let o=n==null?32:Number(n);if(!Number.isInteger(o)||o<0||o>32)return null;let s=o===0?0:4294967295<<32-o>>>0;return a=>(a&s)===(r&s)}function k0(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(xN).filter(Boolean)}function vN(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function x0(t){let e=k0(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:vN,allowed(r){if(n)return!0;let o=uf((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function v0(t,e){let n=k0(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=uf(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import S0 from"node:crypto";import oc from"node:fs";import SN from"node:path";function T0(t=process.env,{stateDir:e=Bs,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=SN.join(e,"api-token");try{let a=oc.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=S0.randomBytes(32).toString("base64url");try{oc.mkdirSync(e,{recursive:!0}),oc.writeFileSync(o,s+`
412
- `,{mode:384}),oc.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Us(t){let e=t?.socket?.remoteAddress||"";if(!e)return!1;let n=e.replace(/^::ffff:/,"");return n==="::1"||n==="127.0.0.1"||/^127\./.test(n)}function A0(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:S0.timingSafeEqual(n,r)}function _0(t,e=null){let n=t?.headers?.["x-bostrat-token"];if(typeof n=="string"&&n.trim())return n.trim();let r=t?.headers?.authorization||"",o=/^Bearer\s+(.+)$/i.exec(r)?.[1];if(o)return o.trim();try{return(e??new URL(t.url,"http://node")).searchParams.get("token")||""}catch{return""}}var TN=[/^\/api\/github\/token\b/],AN=t=>TN.some(e=>e.test(t));function E0({token:t,enforce:e=!0,log:n=console}={}){return function(o,s,a){let i=o.path||o.url||"";if(!i.startsWith("/api"))return a();let c=_0(o);return A0(c,t)?a():AN(i)?(n.log?.(`[perimeter] refused ${o.method} ${i} \u2014 sensitive route requires the node API token`),s.status(401).json({error:"this route requires the node API token"})):!e||Us(o)?a():(n.log?.(`[perimeter] refused ${o.method} ${i} from ${o.socket?.remoteAddress||"?"} \u2014 no node API token`),s.status(401).json({error:"unauthorized"}))}}function I0(t,e,{enforce:n=!0}={}){return!n||A0(_0(t),e)?!0:Us(t)}import _N from"node:http";import EN from"node:net";function R0(t){if(!t)return null;let e=String(t).replace(/:\d+$/,"").toLowerCase();if(/^[\d.]+$/.test(e)||e.includes(":"))return null;let n=e.split(".");if(n.length<3)return null;let r=n[0];return/^[a-z0-9-]+$/.test(r)?r:null}function sc(t){let e=R0(t.headers.host);return e?{slug:e,view:Wl(e)}:null}var IN=t=>`<!doctype html><meta charset="utf-8">
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">
413
413
  <meta http-equiv="refresh" content="3">
414
414
  <title>${t.name} \u2014 starting</title>
415
415
  <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
416
416
  <div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
417
- <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function $0(t,e,n){let r=sc(t);if(!r)return n();let{view:o,slug:s}=r;if(!o)return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: no view named "${s}"`);if(o.state==="starting")return e.statusCode=503,e.setHeader("content-type","text/html"),e.end(IN(o));if(o.state!=="live"&&o.state!=="unhealthy")return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...t.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":t.headers.host,"x-forwarded-proto":t.headers["x-forwarded-proto"]||"http"},i=_N.request({host:"127.0.0.1",port:o.port,method:t.method,path:t.url,headers:a},c=>{e.writeHead(c.statusCode||502,c.headers),c.pipe(e)});i.on("error",()=>{e.headersSent?e.destroy():(e.statusCode=502,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),t.pipe(i),t.on("aborted",()=>i.destroy())}function O0(t,e,n){let r=sc(t);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return e.write(`HTTP/1.1 404 Not Found\r
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
418
418
  \r
419
419
  bostrat: no live view "${s}"\r
420
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
421
421
  `)+`\r
422
422
  \r
423
- `),n?.length&&a.write(n),e.pipe(a),a.pipe(e)}),i=()=>{a.destroy(),e.destroy()};return a.on("error",i),e.on("error",i),!0}function M0(t,e){let n=typeof e=="string"?{host:e}:e||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${t.slug}.${r.replace(/:\d+$/,"")}/`:`http://${t.slug}.${RN(n.host)}/`}function RN(t){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let e=String(t||"").replace(/:\d+$/,"").toLowerCase();if(!e||!e.includes(".")||/^[\d.]+$/.test(e))return"bostrat.globopi";let n=e.split(".");return R0(e)&&Wl(n[0])?n.slice(1).join("."):e}var C0=10,N0=200,$N=120,ON=24,MN=/^\d{4}-\d{2}-\d{2}$/;function df(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var CN=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function P0(t,e=Date.now()){if(!t)return e-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(t).trim());if(n)return e-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(t);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${t}" \u2014 use 24h, 7d, or an ISO date`)}function NN(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=es(t);if(e.length)throw new Error(`changelog entry appears to contain a secret VALUE (${e.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of t.split(`
424
- `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,$N)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${ON}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>N0)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${N0}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>C0)throw new Error(`${r.length} bullets \u2014 over the ${C0}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function D0(t,{repos:e=df()}={}){let n=new Set(e);return(await Ee()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=t&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,mergedAt:r.resolvedAt??null}))}async function B0(t,{md:e,day:n=null}){let r=NN(e),o=n==null||n===""?CN():String(n);if(!MN.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await $({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}import{execFile as PN}from"node:child_process";import{promisify as DN}from"node:util";var pf=DN(PN),j0=3e4,BN=30*6e4,jN=j0,LN=10*6e4,FN=10080*6e4,UN=60*6e4,HN=8,WN=1440*6e4,qN=3,L0="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ff=60*6e4,KN=15*6e4,ac=new Map,ic=new Map,GN=/could not resolve|not found|no pull requests?/i,VN=t=>GN.test(String(t?.message||t||""));function zN(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<ff)}var F0={fast:jN,slow:LN,decay:UN};function U0(t,e){return t.armedAt&&e-t.armedAt<BN||zN(t,e)?"fast":e-(t.firstSeen||0)>FN?"decay":"slow"}function JN(t,e){return F0[U0(t,e)]}function Ks(t,e,n,r){let o=t.lanes?.get(K(e.repo,e.number))||{};return Ri({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function XN(t,e){Je("watch");try{let{stdout:n}=await pf("gh",["pr","view",String(e),"--repo",t,"--json",L0],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await me({slug:t})}});return JSON.parse(n||"null")}catch(n){throw An(n)&&_n(n),n}}async function YN(t){Je("watch");try{let{stdout:e}=await pf("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",L0],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await me({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw An(e)&&_n(e),e}}async function ZN(){let t=new Map;for(let e of await st().catch(()=>[]))t.set(e.id,e);return t}async function QN(t,e){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function tP(t,e){let n=await fr(t);return n?ro(n,e):null}async function eP(t,e,n){let r=await fr(t);if(!r)throw new Error(`no local checkout for ${t}`);return oo(r,e,{method:n})}async function nP(t,e){let n=await fr(t);if(!n)throw new Error(`no local checkout for ${t}`);return Ss(n,e)}async function rP(t,e){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function vo(t,e,n,r,o){if(!await r.disarm(t.repo,t.number).catch(()=>null))return;let a=n.get(t.trackId);a&&await r.emitDisarmed(a,{number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo,reason:o}).catch(()=>{})}var oP=10*6e4;async function sP(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>WN){await vo(t,null,e,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(t.repo,t.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await vo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await vo(t,o,e,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(t.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<oP)return;await vo(t,o,e,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(t.repo,t.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(t.repo,t.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await vo(t,o,e,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(t.repo,t.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await vo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await H0(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Hs(t,e,n){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function aP(t,e){try{return await pf("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ws(t,e,n,r=()=>!0){return(await B(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var qs=(t,e)=>({number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo});async function iP(t,e,n,r){let o=r.now??Date.now(),s=e.headRefOid||null,a=n.get(t.trackId)||null;if(s&&(t.ciFix?.headSha&&s!==t.ciFix.headSha&&await ei(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await ri(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait),l=!!t.baseFoldAt&&o-t.baseFoldAt<KN;await zw(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await Vw(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ws(a,"pushed",t.number,f=>f.payload?.sha===s)&&await Hs(a,"pushed",{...qs(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>ff)await ni(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await ni(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Ks(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Ws(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await Hs(a,"ci-fixed",{...qs(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await ni(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>ff)await oi(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await oi(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Ks(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Ws(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Hs(a,"reconciled",{...qs(t,e),base:i.baseRefName||null}).catch(()=>{}),await oi(t.repo,t.number).catch(()=>{})}}}}async function cP(t,e,n,r){let o=Ks(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Ws(s,"closed",t.number)&&await Hs(s,"closed",{...qs(t,e),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(t.repo,t.number,"closed").catch(()=>{}),await r.mark(t.repo,t.number,"closed").catch(()=>{})}async function lP(t,e,n){Ks(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Ws(r,"lost",t.number)&&await Hs(r,"lost",qs(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function H0(t,e,n,r,o={}){let s=Ks(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await B(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(t.number))||await r.emitMerged(a,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(t.repo,t.number).catch(()=>{}),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function uP(t={}){let{now:e=Date.now(),auth:n=ud,view:r=XN,list:o=YN,links:s=Xw,trackMap:a=ZN,emitMerged:i=QN,mark:c=vn,archiveIfArmed:l=In,notifyIfArmed:u=Rn,notifyIfResolved:f=Ld,batchThreshold:d=qN,detail:p=tP,merge:m=eP,updateBranch:h=nP,lookup:y=Bt,disarm:k=ds,emitDisarmed:v=rP,localHasCommit:I=aP}=t;if(!(await n().catch(()=>null))?.authenticated||ki()>0)return 0;let O=(await s().catch(()=>[])).filter(T=>e-(ac.get(K(T.repo,T.number))||0)>=JN(T,e));if(!O.length)return 0;let M=Date.now(),S=new Map,N=new Map;for(let T of O){let g=U0(T,e);S.set(K(T.repo,T.number),{lane:g,intervalMs:F0[g]}),ac.set(K(T.repo,T.number),e),N.has(T.repo)||N.set(T.repo,[]),N.get(T.repo).push(T)}let D=await a().catch(()=>new Map),it={now:e,lanes:S,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:h,lookup:y,disarm:k,emitDisarmed:v,localHasCommit:I};for(let[T,g]of N){let w,A=new Set;if(g.length>d)w=await o(T).catch(()=>null);else{w=new Map;for(let E of g)try{let J=await r(T,E.number);J?.number!=null&&w.set(Number(J.number),J)}catch(J){VN(J)&&A.add(K(E.repo,E.number))}}if(w)for(let E of g){let J=K(E.repo,E.number),W=w.get(Number(E.number));if(!W){if(A.has(J)){let at=(ic.get(J)||0)+1;ic.set(J,at),at>=HN&&(await lP(E,D,it),ic.delete(J),ac.delete(J))}continue}ic.delete(J),W.state==="MERGED"||W.state==="CLOSED"?(W.state==="MERGED"?await H0(E,W,D,it):await cP(E,W,D,it),ac.delete(J)):(await iP(E,W,D,it).catch(()=>{}),E.autoMerge&&await sP(E,D,it).catch(()=>{}))}}let ct={};for(let{lane:T}of S.values())ct[T]=(ct[T]||0)+1;return console.log(`[pr-watch] probed ${O.length} link(s) (${Object.entries(ct).map(([T,g])=>`${T} ${g}`).join(", ")}) in ${Date.now()-M}ms`),O.length}function W0(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await uP()}catch{}finally{t=!1}}},j0)}import nn from"node:fs/promises";import So from"node:path";import X0 from"node:os";import q0 from"node:fs/promises";import cc from"node:path";import{execFile as dP}from"node:child_process";import{promisify as fP}from"node:util";import{fileURLToPath as pP}from"node:url";var mP=fP(dP),G0=cc.dirname(pP(import.meta.url)),hP=cc.resolve(G0,"..","..","client","dist"),wP="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",gP=new Map([...wP].map((t,e)=>[t,e]));function yP(t){let e=[],n=0,r=0;for(let o of t){let s=gP.get(o);if(s===void 0)return e;n+=(s&31)<<r,s&32?r+=5:(e.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return e}function bP(t){let e=[],n=0,r=0,o=0;for(let s of t.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=yP(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}e.push(a)}return e}function kP(t,e){if(!t||t.length===0||t[0][0]>e)return null;let n=0,r=t.length-1;for(;n<r;){let o=n+r+1>>1;t[o][0]<=e?n=o:r=o-1}return t[n]}var K0=new Map;async function xP(t){let e;try{e=await q0.stat(t)}catch{return null}let n=K0.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await q0.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:bP(r.mappings||"")};return K0.set(t,o),o}catch{return null}}var mf=null;function vP(){return mf||(mf=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):mP("git",["rev-parse","--short","HEAD"],{cwd:cc.resolve(G0,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),mf}var SP=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function TP(t,{distDir:e=hP}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(SP)){let[a,i,c,l]=s,u=await xP(cc.join(e,"assets",`${i}.map`)),f=u?kP(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function V0(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await vP();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await TP(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var AP=()=>process.env.BOSTRAT_STATE_DIR||So.join(process.env.HOME||X0.tmpdir(),".bostrat"),Ln=()=>So.join(AP(),"telemetry"),Y0=()=>So.join(Ln(),".relay-offsets.json"),_P=50,EP=14,IP=3e4,z0=500,Yt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function RP(t,e){if(!t||typeof t!="object")return null;let n=Yt(t.name,64);if(!n)return null;let r=Number.isFinite(t.ts)?t.ts:e,o={name:n,ts:Math.abs(r-e)<168*3600*1e3?r:e,receivedAt:e,anonId:Yt(t.anonId,64),sessionId:Yt(t.sessionId,64),userId:Yt(t.userId,64),release:Yt(t.release,64),appVersion:Yt(t.appVersion,32),route:Yt(t.route,128),deviceClass:t.deviceClass==="phone"||t.deviceClass==="desktop"?t.deviceClass:""};return t.props&&typeof t.props=="object"&&!Array.isArray(t.props)&&(JSON.stringify(t.props).length<=8192?o.props=t.props:o.props={clamped:!0}),t.error&&typeof t.error=="object"&&(o.error={kind:Yt(t.error.kind,256),message:Yt(t.error.message,8192),stack:Yt(t.error.stack,32768),fingerprint:Yt(t.error.fingerprint,64)}),Array.isArray(t.breadcrumbs)&&(o.breadcrumbs=t.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Yt(s?.n,64),r:Yt(s?.r,128)}))),o}var Z0=t=>new Date(t).toISOString().slice(0,10),J0=0;async function Q0(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,_P):[]).map(s=>RP(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await V0(s);await nn.mkdir(Ln(),{recursive:!0});let o=So.join(Ln(),`${Z0(e)}.ndjson`);return await nn.appendFile(o,r.map(s=>JSON.stringify(s)+`
425
- `).join(""),"utf8"),e-J0>6*3600*1e3&&(J0=e,$P(e).catch(()=>{})),r.length}async function $P(t=Date.now()){let e=Z0(t-EP*24*3600*1e3),n=[];try{n=await nn.readdir(Ln())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<e);if(!r.length)return;for(let s of r)await nn.rm(So.join(Ln(),s),{force:!0});let o=await ex();for(let s of r)delete o[s];await nx(o)}var lc=new Map;function tx(t,e=Date.now()){let n=t||"unknown",r=lc.get(n);return r||(lc.size>512&&lc.clear(),r={tokens:40,at:e},lc.set(n,r)),r.tokens=Math.min(40,r.tokens+(e-r.at)/6e4*20),r.at=e,r.tokens<1?!1:(r.tokens-=1,!0)}async function ex(){try{return JSON.parse(await nn.readFile(Y0(),"utf8"))||{}}catch{return{}}}async function nx(t){await nn.mkdir(Ln(),{recursive:!0}),await nn.writeFile(Y0(),JSON.stringify(t),"utf8")}function OP(t,{env:e,host:n}={}){let r=t.name==="app.error"&&t.error,o={};for(let[a,i]of[["event",t.name],["route",t.route],["session",t.sessionId],["anon",t.anonId],["user",t.userId],["release",t.release],["appVersion",t.appVersion],["device",t.deviceClass]])i&&(o[a]=i);let s={timestamp:new Date(t.ts).toISOString(),service:"bostrat-client",source:"treetop-js",env:e||process.env.BOSTRAT_ENV||"pi",host:n||X0.hostname(),level:r?"error":"info",message:r?`${t.error.kind||"Error"}: ${t.error.message||"(no message)"}`.slice(0,8192):t.name,tags:o,attributes:{...t.props?{props:t.props}:{},receivedAt:t.receivedAt}};return r&&(s.error={kind:t.error.kind,message:t.error.message,stack:t.error.stack},t.error.fingerprint&&(s.fingerprint=t.error.fingerprint),t.error.symbolicated&&(s.attributes.symbolicated=!0),t.breadcrumbs&&(s.attributes.breadcrumbs=t.breadcrumbs)),s}async function MP({fetchImpl:t=fetch}={}){let e=await hw();if(!e)return{sent:0,disabled:!0};let n;try{n=(await nn.readdir(Ln())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await ex(),o=0;for(let s of n){let a=So.join(Ln(),s),i=r[s]||0,c;try{let d=await nn.open(a,"r");try{let p=(await d.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
426
- `).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=z0){let p=f.slice(d,d+z0).map(h=>OP(h)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await nx(r)}return{sent:o,disabled:!1}}var hf=null,wf=!1;function rx(){hf||(hf=setInterval(async()=>{if(!wf){wf=!0;try{await MP()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{wf=!1}}},IP),hf.unref?.())}import dc from"node:fs/promises";import cx from"node:path";import CP from"node:os";var lx=()=>process.env.BOSTRAT_STATE_DIR||cx.join(process.env.HOME||CP.tmpdir(),".bostrat"),ux=()=>cx.join(lx(),"dictation-glossary.json"),NP=100,PP=500,DP=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],fc=null,gf=null,yf=Promise.resolve();function ox(){let t={},e=Date.now();for(let n of DP)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function dx(){return fc||(gf??=dc.readFile(ux(),"utf8").then(t=>JSON.parse(t)?.terms??ox()).catch(()=>ox()).then(t=>fc=t).finally(()=>gf=null),gf)}async function sx(){let t=JSON.stringify({terms:fc},null,2),e=ux(),n=`${e}.${process.pid}.tmp`;await dc.mkdir(lx(),{recursive:!0}),await dc.writeFile(n,t),await dc.rename(n,e)}function BP(){return yf=yf.then(sx,sx),yf.catch(()=>{})}async function bf(){return Object.values(await dx()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var jP=t=>Math.max(1,Math.ceil(t.length/4));async function LP(){let t=[],e=0;for(let{term:n}of await bf()){let r=jP(n);if(!(e+r>PP)&&(t.push(n),e+=r,t.length>=NP))break}return t}async function FP(t,e){let n=await dx(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var ax=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),uc=t=>t.toLowerCase();function ix(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function fx(t,e){let n=ax(t),r=ax(e);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let f=s-1;f>=0;f--)a[u][f]=uc(n[u])===uc(r[f])?a[u+1][f+1]+1:Math.max(a[u+1][f],a[u][f+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)uc(n[c])===uc(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(ix(r[l])&&i.add(r[l]),l++);for(;l<s;l++)ix(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await FP(u,"learned");return await BP(),[...i]}var Gs=null;function UP(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return t===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:t===401||t===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${t}: ${e.slice(0,300)}`}}async function px(){if(!Gs)return null;let t=await rs();if(!t||t.apiKey.slice(-6)!==Gs.keyTail)return null;let{keyTail:e,...n}=Gs;return n}async function mx(t,e){let n=await rs();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await LP())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":e||"application/octet-stream"},body:t});if(!o.ok){let i=await o.text().catch(()=>""),c=UP(o.status,i);c.persistent&&(Gs={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return Gs=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var kf={"gpt-realtime-2.1":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2":{textIn:4,textInCached:.4,textOut:24,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime":{textIn:4,textInCached:.4,textOut:16,audioIn:32,audioInCached:.4,audioOut:64},"gpt-realtime-2.1-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20},"gpt-realtime-mini":{textIn:.6,textInCached:.06,textOut:2.4,audioIn:10,audioInCached:.3,audioOut:20}};function HP(t){if(!t)return null;let e=String(t).trim();if(kf[e])return e;let n=null;for(let r of Object.keys(kf))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function WP(t,e={}){let n=kf[HP(t)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(e[o])||0)*n[o];return r/1e6}function qP(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.error?.code||n?.error?.type||"";return t===429&&/insufficient_quota/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"The OpenAI account is out of credit \u2014 voice chat is down until you top up at platform.openai.com or drop a new key into ~/.bostrat/integrations.json."}:t===401||t===403?{status:502,code:"key_invalid",persistent:!0,message:"OpenAI rejected the API key (revoked, or missing Realtime access) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"mint_failed",persistent:!1,message:`OpenAI realtime session mint failed (${t}): ${e.slice(0,300)}`}}var Vs=null;async function KP(){if(!Vs)return null;let t=await Xa();if(!t||t.apiKey.slice(-6)!==Vs.keyTail)return null;let{keyTail:e,...n}=Vs;return n}async function GP(t){let e=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${t.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:t.model,audio:{output:{voice:t.voice}}}})});if(!e.ok){let o=await e.text().catch(()=>""),s=qP(e.status,o);s.persistent&&(Vs={code:s.code,message:s.message,at:Date.now(),keyTail:t.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}Vs=null;let n=await e.json(),r=n?.value??n?.client_secret?.value;if(!r){let o=new Error("OpenAI returned no client secret");throw o.status=502,o.code="mint_failed",o}return{clientSecret:{value:r,expiresAt:n?.expires_at??n?.client_secret?.expires_at??null}}}var VP={openai:GP};async function hx(){let t=await Xa();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await KP()}:{configured:!1}}async function wx(){let t=await Xa();if(!t){let n=new Error("Voice chat is not configured \u2014 add openai.apiKey to ~/.bostrat/integrations.json");throw n.status=503,n.code="not_configured",n}let e=await VP[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function gx(t,{model:e,usage:n={},seconds:r}={}){let o=c=>Math.max(0,Math.round(Number(c)||0)),s={textIn:o(n.textIn),textInCached:o(n.textInCached),textOut:o(n.textOut),audioIn:o(n.audioIn),audioInCached:o(n.audioInCached),audioOut:o(n.audioOut)},a=WP(e,s),i=await $(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var Sx=ft.dirname(JP(import.meta.url)),yx=Number(process.env.PORT)||3362,pc=Wk(process.env),Tx=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,To=x0(process.env.BOSTRAT_ALLOW_IPS),Af=v0(process.env.BOSTRAT_VIEW_FRONT_CIDRS,sc),Ax=T0(),_x=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=fe();x.disable("x-powered-by");x.use((t,e,n)=>{if(Us(t)||To.allowed(To.clientIp(t))||Af.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use($0);x.use(E0({token:Ax,enforce:_x}));x.use("/api/mocks",fe.json({limit:"1mb"}));x.use("/api/mocks",fe.raw({type:"image/png",limit:"24mb"}));x.use("/api/mocks",fe.raw({type:["video/mp4","video/webm"],limit:"105mb"}));x.use("/api/runbooks",fe.json({limit:"1mb"}));x.use("/api/telemetry",fe.json({limit:"256kb"}));x.use("/api/hook",fe.json({limit:"2mb"}));x.use("/api/dictation",fe.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",fe.raw({type:()=>!0,limit:"25mb"}));x.use(fe.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:zt,hostTmuxAlive:await un(),autoTriggerPct:Tx})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await Ce()})});x.post("/api/sessions",async(t,e)=>{let{displayName:n,cwd:r}=t.body||{};if(typeof n!="string")return e.status(400).json({error:"displayName (string) required"});try{let o=await qn(n,r||void 0);e.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;e.status(a).json({error:s})}});x.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});if(!await tt(n))return e.status(404).json({error:"session not found"});try{await we(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});x.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});let r=await tt(n);if(!r)return e.status(404).json({error:"session not found"});let o=await jo(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});x.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});try{let r=await oh(n);e.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){e.status(409).json({error:r?.message||String(r)})}});x.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=Pa(n);if(!r)return e.json({running:!1});e.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});var bx=10;x.get("/api/tracks",async(t,e)=>{let n=await st(),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>bx){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,bx).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:Bi,agentAvailability:await Cd(),tickets:await Za()})});x.get("/api/cost",async(t,e)=>{try{let n=await st();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await Yk(n);if(r.cursorUsage=ad(),t.query.format==="text")return e.type("text/plain").send(Zk(r));e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/roundup",async(t,e)=>{try{e.json(await e0(await st(),{window:t.query.window,from:t.query.from,to:t.query.to}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/spend",async(t,e)=>{try{let n=await a0(await st(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=ad(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=hs(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json(hs())});x.post("/api/usage/caps",async(t,e)=>{try{e.json(await pi(t.body?.caps??{},{source:"local"}))}catch(n){e.status(400).json({error:n?.message||String(n)})}});x.get("/api/usage/limits",async(t,e)=>{try{let n=t.query.fresh==="1"||t.query.fresh==="true";e.json(await w0(await st(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs",async(t,e)=>{try{e.json(await ng(await st()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs/:key",async(t,e)=>{try{let n=await rg(await st(),t.params.key);if(!n)return e.status(404).json({error:"unknown arc"});e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/arcs/backfill-beats",async(t,e)=>{try{e.json({ok:!0,...await kk()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/hook",async(t,e)=>{try{let{mode:n,target:r}=await $f(t.get(Qs),{byToken:Jb,legacy:()=>Ai(t.body?.session_id,t.body?.cwd)});if(n==="refused")return ta("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});n==="token"&&t.body?.session_id&&r.box?.sessionId&&t.body.session_id!==r.box.sessionId&&ta("mismatch",{track:r.track.id,box:r.box.name}),ta(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await Hk(t.body||{},()=>r);e.json({ok:!0,events:o.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.post("/api/telemetry",async(t,e)=>{try{if(!tx(To.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await Q0(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.get("/api/repos",async(t,e)=>{try{let n=await Oe(),r=new Set((await Promise.all(n.map(a=>et(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Ro()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));e.json({repos:[...n,...s]})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/github/token",async(t,e)=>{try{e.json(await oa(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/github/status",(t,e)=>{e.json({configured:ra(),calls:Oy(),latency:nb()})});x.post("/api/github/refresh",(t,e)=>{Ff(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await y0(String(t.query.q||""),{max:Number(t.query.max)||10});e.json({messages:n})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/gmail/messages/:id",async(t,e)=>{try{e.json({message:await b0(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function _f(t,e,n){let r=t.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>e&&o?.base===e||n&&o?.repo===n):!r?.base||!e||r.base===e||n&&r.repo===n}async function mc({repo:t=null,number:e=null,branch:n=null}){let r=t?await et(t):null;if(r&&e!=null){let s=await Bt(r,e),a=s&&await Q(s.trackId);if(a)return a}return n?(await st()).find(s=>s.workspace?.branch===n&&_f(s,t,r))??null:null}async function Ex({repo:t=null,number:e=null,branch:n=null}){let r=await mc({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await et(t).catch(()=>null):null;return(await st()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&_f(a,t,o))??null}async function de(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await mc({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await et(s).catch(()=>null):null);await $({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function QP(t){let e=(await st()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await et(n.path);if(r)for(let o of n.prs){if(await Bt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&_f(a,n.path,r));s&&await Jt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}x.get("/api/git/prs",async(t,e)=>{try{let n=await lb();await QP(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=t.body||{};try{if(s){let h=o||"merge";if(!vs.has(h))throw new Error(`invalid merge method: ${h}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let k=await et((await Xe(n)).path);if(!k)throw new Error("repo has no GitHub remote");let v=await mc({repo:n,number:y,branch:l}).catch(()=>null);if(!v)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await Ss(n,y),await Jt({repo:k,number:y,trackId:v.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ls(k,y,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await rr(k,y,i===!0),await us(k,y,c===!0&&i!==!0),Re(),await de("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:v,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:y,auto:!0})}let d=await oo(n,r,{method:o||"squash"});await de("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await et(n).catch(()=>null),m=!1;if(p&&(i===!0&&await rr(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await us(p,d.number,!0).catch(()=>{}),await Rn(p,d.number).catch(()=>{}),m=await In(p,d.number).catch(()=>!1),await vn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let h=await mc({repo:n,number:d.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await mo(h.id).catch(()=>{})}e.json(d)}catch(d){let p=d?.message||String(d),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;e.status(m).json({error:p})}});x.post("/api/git/prs/disarm-auto",async(t,e)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=t.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await et((await Xe(n)).path).catch(()=>null),l=c?await ds(c,i).catch(()=>null):null;try{await pd(n,i)}catch(u){if(!l)throw u}Re(),await de("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),e.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await et(n).catch(()=>null),s=await pb(n,{method:r||"merge",onMerged:a=>{de("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Rn(o,a.number).catch(()=>{}).then(()=>In(o,a.number)).catch(()=>{}).then(()=>vn(o,a.number,"merged")).catch(()=>{})}});e.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;e.status(a).json({error:s})}});x.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:fb()})});x.post("/api/git/prs/merge-track",async(t,e)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=t.body||{};try{let i=a||"merge";if(!vs.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return e.status(400).json({error:"trackId required"});let c=await Q(n);if(!c)return e.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Ee()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return e.status(409).json({error:"no open PRs on this Track"});for(let d of l)await rr(d.repo,d.number,o===!0).catch(()=>{}),await us(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let p={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(p);try{let m=await fr(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let h=await ro(m,d.number);f.push({r:p,l:d,repoPath:m,pr:h,plan:ub(h,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:d,l:p,repoPath:m,pr:h,plan:y}of db(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await oo(m,p.number,{method:i}),await de("merged",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c}),await Rn(p.repo,p.number).catch(()=>{}),await In(p.repo,p.number).catch(()=>{}),await vn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await Ss(m,p.number).catch(()=>{}),d.action="updated-armed"),!await ls(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await de("auto-armed",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){d.ok=!1,d.action="failed",d.reason=k?.message||String(k)}if(Re(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await rr(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await us(d.repo,d.number,!1).catch(()=>{});return e.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}e.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/disarm-track",async(t,e)=>{let{trackId:n}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let r=await Q(n);if(!r)return e.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Ee()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ds(a.repo,a.number).catch(()=>null),c=await fr(a.repo).catch(()=>null);c&&await pd(c,a.number).catch(()=>{}),i&&(await de("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}Re(),e.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let i=await Q(n);if(!i)return e.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=qt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(h=>{let y=u.find(k=>k.name===h||k.repo===h||k.base===h||k.dir===h);if(!y)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return y});else if(f=await bs(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let h of f)try{let y=await hd(h.dir,{title:r,body:o,base:s}),k=h.repo||await et(h.dir).catch(()=>null);k&&y.number&&await Jt({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await de("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??h.name}}),p.push({repo:h.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:h.name,slug:h.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(h=>h.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(h=>`${h.repo}: ${h.error}`).join("; "),results:p})}let c=await hd(i.cwd,{title:r,body:o,base:s}),l=await et(i.cwd).catch(()=>null);l&&c.number&&await Jt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await de("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),e.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;e.status(l).json({error:c})}});x.get("/api/tracks/:id/repos",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await bs(n),o=new Map(r.map(a=>[a.base,a])),s=qt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});e.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/repos",async(t,e)=>{let{repo:n}=t.body||{};try{if(!n||typeof n!="string")return e.status(400).json({error:"repo (path or owner/name) required"});let r=await dk(t.params.id,n);e.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.delete("/api/tracks/:id/repos/:name",async(t,e)=>{try{let n=await fk(t.params.id,t.params.name);e.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;e.status(o).json({error:r})}});x.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await ro(n,r),s=await et(n).catch(()=>null),a=await Ex({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=qt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await mb(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ft.join("ci",i.name):"ci",f=ft.join(a.cwd,".bostrat",u),d=ft.join(".bostrat",u,`pr-${o.number}-fail.log`);await Gt.promises.mkdir(f,{recursive:!0}),await Gt.promises.writeFile(ft.join(a.cwd,d),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
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
427
427
  ${k.log??"(no fetchable log \u2014 see link)"}
428
428
  `).join(`
429
- `));let p=l.map(k=>k.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",h=await ue(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${d} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await Jt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await qw(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await de("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),e.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(k=>k.name),log:d})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});x.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await ro(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await et(n).catch(()=>null),a=await Ex({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=qt(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await ue(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),u=s;u&&(await Jt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Kw(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await de("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),e.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});x.post("/api/tracks/:id/pr-offer",async(t,e)=>{let{offerId:n,action:r}=t.body||{};try{let o=null;if(Array.isArray(t.body?.actions)){if(o=yy.filter(f=>t.body.actions.includes(f)),!o.length)return e.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return e.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=o!=null&&t.body?.notify===!0,a=await Q(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await B(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return e.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let l=null;o&&(l=await ue(a.id,gi(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await $({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&nk(a.id,{actions:o}),e.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});var zs=t=>e=>({...e,url:M0(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await Wa({trackId:t.query.track||void 0});e.json({views:n.map(zs(t)),maxLive:yn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await zh(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:zs(t)(n),session:jr(n.slug)})});x.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await Q(n):null;if(!i&&s&&(i=(await st()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await ql({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});e.json({ok:!0,view:zs(t)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);e.status(l).json({error:c})}});x.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:zs(t)(await Jh(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:zs(t)(await Xh(t.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;e.status(o).json({error:r})}});x.delete("/api/views/:id",async(t,e)=>{try{e.json(await Yh(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var tD=[["linear",Qn,Yl],["jira",Ja,Jl]],xf=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await zl(),r={};for(let[o,s,a]of tD){let i=await s();if(!i){xf.delete(o),r[o]={configured:!1};continue}let c=xf.get(o);if(!c||Date.now()-c.at>300*1e3){let l;try{l={configured:!0,ok:!0,...await a(i)}}catch(u){l={configured:!0,ok:!1,error:u?.message||String(u)}}c={at:Date.now(),body:l},xf.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await zl()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await mw(o),i=n?await Q(n):null;!i&&r&&(i=(await st()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ft.sep)))||null),i&&await $({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await Q(n):null;if(!s&&r&&(s=(await st()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ft.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await Hr(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});x.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await Q(r):null;if(!o&&n&&(o=(await st()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ft.sep)))||null),!o)return e.status(400).json({error:"track (or a cwd inside a Track workspace) required"});e.json({trackId:o.id,configured:await Rw(),ticket:await _e(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});x.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await _e(t.params.id)})});x.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Hr(n,(t.body||{}).ref,{source:"api"});e.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);e.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});x.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await lu(t.params.id)})});var wc=t=>/no ticket linked|unknown offer|unknown state/i.test(t)?404:/already answered/i.test(t)?409:/required|no ticketing/i.test(t)?400:502;x.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await Cw(t.params.id))}catch(n){let r=n?.message||String(n);e.status(wc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await uu(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(wc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Nw(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(wc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await Q(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await Pw(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(wc(s)).json({error:s})}});x.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await Q(n):null;if(!s&&r&&(s=(await st()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ft.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,trackId:s.id,proposal:await Bw(s,o)})}catch(s){let a=s?.message||String(s);e.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});x.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await Dw()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});x.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await Q(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await jw(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);e.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});x.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Xn({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/mocks/:id",async(t,e)=>{let n=await Be(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});x.get("/api/mocks/:id/png",async(t,e)=>{let n=await Be(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ae(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Jo(n,r,"png");if(!o)return e.status(404).json({error:"no image for this version (poster extraction failed)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/media",async(t,e)=>{let n=await Be(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ae(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Jo(n,r,"video");if(!o)return e.status(404).json({error:"this version has no video (image or html sourced)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"video artifact missing"})})});x.get("/api/mocks/:id/html",async(t,e)=>{let n=await Be(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ae(n,t.query.v??null);if(!r?.htmlRel)return e.status(404).json({error:"this version has no HTML source (image-sourced)"});e.set("content-security-policy","sandbox"),e.sendFile(ee(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});x.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&ja(n)?n:null,o=n&&!r?n:null,s=n?t.query:t.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await Q(a):null;if(!f&&c&&(f=(await st()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+ft.sep)))||null),!f)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(m=>m.name===f.leadBox),p=await Eh({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});e.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);e.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&ja(n)?n:null;try{let o=await Ih(t.params.id,{html:n?null:(t.body||{}).html,image:r,video:n&&!r?n:null,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;e.status(a).json({error:s})}});x.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Oh(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await ue(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),e.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;e.status(o).json({error:r})}});x.delete("/api/mocks/:id",async(t,e)=>{try{let n=(await Rb(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Mh(t.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/flows",async(t,e)=>{try{e.json({flows:await Tb({trackId:t.query.track||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/flows/:id",async(t,e)=>{let n=await Ab(t.params.id);if(!n)return e.status(404).json({error:"flow not found"});e.json({flow:n})});x.post("/api/flows",async(t,e)=>{let{trackId:n,name:r,steps:o,cwd:s}=t.body||{};try{let a=n?await Q(n):null;if(!a&&s&&(a=(await st()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await _b({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;e.status(c).json({error:i})}});x.post("/api/flows/:id/update",async(t,e)=>{try{let{steps:n,note:r}=t.body||{};e.json({ok:!0,flow:await Eb(t.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;e.status(o).json({error:r})}});x.delete("/api/flows/:id",async(t,e)=>{try{e.json(await Ib(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await st(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ft.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await Ga({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/runbooks/:id",async(t,e)=>{let n=await Gl(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});x.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Gl(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=nw(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),e.sendFile(He(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});x.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await Q(n):null;if(!a&&s&&(a=(await st()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await ow({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});x.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await sw(t.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;e.status(o).json({error:r})}});x.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await aw(t.params.id,(t.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;e.status(o).json({error:r})}});x.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await iw(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/changelog/draft",async(t,e)=>{try{let n=P0(t.query.since||null);e.json({since:n,repos:df(),prs:await D0(n)})}catch(n){let r=n?.message||String(n);e.status(/unparseable/i.test(r)?400:500).json({error:r})}});x.post("/api/changelog/publish",async(t,e)=>{let{md:n,day:r,trackId:o,cwd:s}=t.body||{};try{let a=o?await Q(o):null;if(!a&&s&&(a=(await st()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+ft.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await B0(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var eD=120,vf=400,hc=null;x.post("/api/notify",async(t,e)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=t.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return e.status(400).json({error:"body (the message) required"});if(i.length>vf)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${vf} cap; a notification is a nudge, put the detail in chat`});let c=es(`${n||""}
430
- ${i}`);if(c.length)return e.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await Q(a):null;if(!l&&s&&(l=(await st()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+ft.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),f=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),d={title:n?f(n,eD):l.name,body:f(i,vf),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await $(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),e.json({ok:!0,notification:d,delivered:hc?hc.status().connected:null})}catch(i){e.status(500).json({error:i?.message||String(i)})}});x.post("/api/dictation",async(t,e)=>{if(!Buffer.isBuffer(t.body)||t.body.length===0)return e.status(400).json({error:"expected a raw audio body"});try{e.json(await mx(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});x.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await rs(),terms:await bf(),problem:await px()})});x.post("/api/dictation/learn",async(t,e)=>{let{transcript:n,finalText:r}=t.body||{};if(typeof n!="string"||typeof r!="string")return e.status(400).json({error:"transcript and finalText are required"});e.json({learned:await fx(n,r)})});x.get("/api/tracks/:id/voice-chat",async(t,e)=>{if(!await Q(t.params.id))return e.status(404).json({error:"track not found"});e.json(await hx())});x.post("/api/tracks/:id/voice-chat/session",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});if(n.archivedAt)return e.status(409).json({error:"track is archived"});e.json(await wx())}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});x.post("/api/tracks/:id/voice-chat/usage",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=t.body||{};e.json({ok:!0,...await gx(n,{model:r,usage:o,seconds:s})})}catch(n){e.status(n?.status||500).json({error:n?.message||String(n)})}});x.post("/api/tracks",async(t,e)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c}=t.body||{};try{let l=await jd({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Hr(l,c,{source:"create"})}catch(f){u=f?.message||String(f)}e.json({ok:!0,track:l,...u?{ticketWarning:u}:{}})}catch(l){let u=l?.message||String(l);e.status(/required|unknown agent|invalid|must be/i.test(u)?400:500).json({error:u})}});x.get("/api/tracks/:id",async(t,e)=>{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:Ix(Ns(t.params.id)),subtasks:ho(t.params.id).map(Js)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await yk(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await lu(t.params.id).catch(()=>{}),e.json(n)}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await mo(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});x.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await vk(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});x.get("/api/tracks/:id/messages",async(t,e)=>{let n=await Nd(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});x.get("/api/tracks/:id/events",async(t,e)=>{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),e.flushHeaders();let r=i=>e.write(`id: ${i.seq}
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}
431
431
  data: ${JSON.stringify(i)}
432
432
 
433
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
434
434
 
435
- `),25e3);t.on("close",()=>{clearInterval(a),s()})});x.get("/api/tracks/:id/search",async(t,e)=>{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=String(t.query.q||"").trim();if(!r)return e.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(t.query.k)||8,1),50),s=Ho(await B(n)),a=_a(s,r,{k:o,role:t.query.role||null,tag:t.query.tag||null});e.json({q:r,indexed:s.N,hits:a.map(i=>({score:Number(i.score.toFixed(3)),seq:i.chunk.seq,ts:i.chunk.ts,role:i.chunk.role,box:i.chunk.boxId,provider:i.chunk.provider,tags:i.chunk.tags,text:i.chunk.text}))})});x.get("/api/memory",async(t,e)=>{e.json({stores:await tp()})});x.delete("/api/memory/:key/:file",async(t,e)=>{let n=String(t.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return e.status(400).json({error:"bad store key"});try{e.json({ok:!0,...await ep(n,String(t.params.file||""))})}catch(r){let o=r?.message||String(r);e.status(/not found/i.test(o)?404:/bad fact/i.test(o)?400:500).json({error:o})}});var nD=["Run a memory consolidation pass on this workspace's `.bostrat/memory/` directory:","1. Read `MEMORY.md` and every fact file.","2. Merge duplicates and facts that describe the same thing (keep the best-written one; fold unique details in; give the survivor a `supersedes:` entry naming what it absorbed, delete the absorbed file, and remove its index line).","3. Resolve contradictions in favor of the newest evidence \u2014 check the code where feasible before deciding.","4. Mark clearly-stale facts: correct them in place or delete them (file + index line).","5. Bump `metadata.modified` (ISO-8601, now) on every fact you touch.","6. Keep `MEMORY.md` one line per fact, under 200 lines.","When done, summarize what you merged, corrected, and deleted \u2014 that summary is the review record."].join(`
436
- `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await ue(t.params.id,nD)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});x.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await Tk(t.params.id)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/mid-turn|terminal|archived/i.test(r)?409:500;e.status(o).json({error:r})}});x.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await ue(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return e.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});x.post("/api/tracks/:id/upload",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(n.archivedAt)return e.status(409).json({error:"track is archived"});if(!n.cwd)return e.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(t.body)?t.body:null,o=await Pb(n.cwd,{filename:t.query.filename,buffer:r});e.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);e.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});x.get("/api/tracks/:id/uploads/:file",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(!n.cwd)return e.status(404).json({error:"track has no workspace"});let r=ft.basename(String(t.params.file||""));if(!r||!vd(ft.extname(r).slice(1)))return e.status(400).json({error:"not an upload path"});e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.sendFile(ft.join(xd(n.cwd),r),o=>{o&&!e.headersSent&&e.status(404).json({error:"upload not found"})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});var rD=/^[0-9a-fA-F-]{8,64}$/;x.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});let{box:r,uuid:o}=t.params,s=Number(t.params.idx);if(!rD.test(o)||!Number.isInteger(s)||s<0)return e.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(d=>d.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return e.status(404).json({error:"image source not available"});let i=Ne(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=zP.createInterface({input:Gt.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let p;try{p=JSON.parse(d)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let f=l?Jc(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return e.status(404).json({error:"image not found"});e.set("content-type",f.mediaType),e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.send(Buffer.from(f.data,"base64"))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await Bd(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});x.delete("/api/tracks/:id/queue",async(t,e)=>{try{let{removed:n,restored:r,draftRev:o}=await Zb(t.params.id);e.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await Pd(t.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code|no answer selected/i.test(a)?409:500;e.status(i).json({error:a})}});x.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await wk({provider:t.params.provider,fresh:t.body?.fresh===!0});e.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);e.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});x.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await Yb(t.params.id,{promptId:n,questionIndex:r}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/no pending prompt|changed since|not running|not supported|out of range|review screen/i.test(s)?409:500;e.status(a).json({error:s})}});x.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n,rev:r}=t.body||{};try{e.json(await ak(t.params.id,typeof n=="string"?n:"",{rev:typeof r=="number"?r:null}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required/i.test(s)?400:500;e.status(a).json({error:s})}});x.get("/api/tracks/:id/usage",async(t,e)=>{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=t.query.fresh==="1"||t.query.fresh==="true";e.json(await nc(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await ik(t.params.id,n))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|unknown model|not running/i.test(o)?400:500;e.status(s).json({error:o})}});x.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await Rk(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Js(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return e.status(429).json({error:"usage_cap",message:i,cap:a.usageCap});let c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;e.status(c).json({error:i})}});x.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:ho(t.params.id).map(Js)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await Xb(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});x.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:Js(qd(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});x.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:Js(Ok(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Js(t){return t?{id:t.id,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,step:t.step,running:!t.finishedAt,box:t.box,branch:t.branch,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}x.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await Ud(t.params.id,n,r!==!1,o);e.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);if(s?.usageCap)return e.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});x.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await rk(t.params.id,t.body||{}))}catch(n){let r=n?.message||String(n);e.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});x.get("/api/tracks/:id/handoff-lead",(t,e)=>{e.json(Ix(Ns(t.params.id))??{running:!1})});function Ix(t){return t?{running:!t.finishedAt,step:t.step,error:t.error,fromBox:t.fromBox,toBox:t.toBox,toAgent:t.toAgent,skipPermissions:t.skipPermissions,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}x.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:fa(),paths:yp,trace:await gp({limit:n})})});var Sf=ft.resolve(Sx,"..","..","client","dist");Gt.existsSync(Sf)&&(x.use(fe.static(Sf)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ft.join(Sf,"index.html"))));var Rx=XP.createServer(x),oD=new ZP({noServer:!0}),$x=(t,e,n)=>{if(!(Us(t)||To.allowed(To.clientIp(t))||Af.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
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
437
437
  \r
438
- `),e.destroy();return}if(O0(t,e,n))return;if(!I0(t,Ax,{enforce:_x})){e.write(`HTTP/1.1 401 Unauthorized\r
438
+ `),e.destroy();return}if(M0(t,e,n))return;if(!R0(t,_x,{enforce:Ex})){e.write(`HTTP/1.1 401 Unauthorized\r
439
439
  \r
440
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
441
441
  \r
442
442
  `),e.destroy();return}let o=r.sessionName;if(!wt(o)){e.write(`HTTP/1.1 400 Bad Request\r
443
443
  \r
444
- `),e.destroy();return}let{cols:s,rows:a}=r;oD.handleUpgrade(t,e,n,i=>Ip(i,o,{cols:s,rows:a}))};Rx.on("upgrade",$x);var Ox=process.env.BOSTRAT_CERT_DIR||ft.join(process.env.HOME||Tf.tmpdir(),".bostrat","certs"),kx=Number(process.env.BOSTRAT_TLS_PORT)||3443,xx=ft.join(Ox,"bostrat.globopi.key"),vx=ft.join(Ox,"bostrat.globopi.crt");if(Gt.existsSync(xx)&&Gt.existsSync(vx)){let t=YP.createServer({key:Gt.readFileSync(xx),cert:Gt.readFileSync(vx)},x);t.on("upgrade",$x),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(kx,pc,()=>{console.log(`[bostrat-agent] https listening on ${pc||"0.0.0.0"}:${kx}`)})}Rx.listen(yx,pc,()=>{console.log(`[bostrat-agent] listening on ${pc||"0.0.0.0"}:${yx}`),console.log(`[bostrat-agent] tmux socket: ${zt}`),console.log(`[bostrat-agent] auto-trigger default: ${Tx}%`),console.log(`[bostrat-agent] ip allowlist: ${To.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Af.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Ek().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Ik().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Sk().then(t=>t&&console.log(`[bostrat-agent] reaped ${t} orphaned box(es) on archived track(s)`)).catch(t=>console.error("[bostrat-agent] archived-orphan sweep failed:",t?.message||t)),hk().then(t=>console.log(`[bostrat-agent] standing instructions refreshed (${t.refreshed} updated, ${t.removed} legacy CLAUDE.md removed${t.codexGlobal?`, codex global ${t.codexGlobal}`:""})`)).catch(t=>console.error("[bostrat-agent] standing-instructions refresh failed:",t?.message||t)),cD(),iD(),Xg({listTracks:st,getTrack:Q}).then(t=>console.log(`[bostrat-agent] spend caps ${t.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(t.spent).length} provider counter(s)`)).catch(t=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",t?.message||t)),d0()&&f0({listTracks:st}),W0(),Lw({resolveTrack:Q}),Uk({listTracks:st,createTrack:jd,archiveTrack:mo,listRepos:Oe,resolveTrack:Q,sayToLead:ue,answerPrompt:Pd,startLeadHandoff:Ud,interruptLead:Bd,listSubtaskJobs:ho,cancelSubtask:qd,readEvents:B,appendEvent:$,knownAgents:Bi}),sD(),Do().then(t=>{t||console.warn(`[boot] no host tmux server on ${zt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),rx(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(hc=Gk({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Tf.hostname(),name:process.env.BOSTRAT_NODE_NAME||Tf.hostname(),listTracks:st,detectAgents:Cd,sampleMetrics:zk}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),vr().catch(()=>{}),uw().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Fw().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),Ph().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),Mb().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),Yw().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{hc?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function sD(){aD(),Ha().catch(()=>{}),setInterval(()=>Ha().catch(()=>{}),1e4)}function aD(){for(let t of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-runbook","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let e=ft.resolve(Sx,"..","bin",t),n=ft.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Gt.existsSync(e))continue;Gt.mkdirSync(n,{recursive:!0});let r=ft.join(n,t);(Gt.existsSync(r)?Gt.readlinkSync(r):null)!==e&&(Gt.rmSync(r,{force:!0}),Gt.symlinkSync(e,r)),console.log(`[bostrat-agent] ${t} CLI linked at ${r}`)}catch(e){console.error(`[bostrat-agent] ${t} CLI link failed:`,e?.message||e)}}function iD(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await st(),r=await c0({tracks:n});r.committed&&console.log(`[state-sync] committed (${r.staged} file(s) staged${r.pushed?", pushed":""})`)}catch{}finally{t=!1}}};setInterval(e,6e4),e()}function cD(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await st();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await Nd(n.id).catch(()=>{}),await hb(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
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)}