bostrat-node 0.2.20 → 0.2.21

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 +146 -146
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,121 +1,121 @@
1
- var tx=Object.defineProperty;var lf=(t,e)=>{for(var n in e)tx(t,n,{get:e[n],enumerable:!0})};import ft from"node:path";import Gt from"node:fs";import $N from"node:readline";import{fileURLToPath as ON}from"node:url";import Ie from"express";import MN from"node:http";import CN from"node:https";import of from"node:os";import{WebSocketServer as NN}from"ws";import{execFile as SS}from"node:child_process";import{promisify as vS}from"node:util";import sa from"node:fs";import{randomUUID as TS}from"node:crypto";import Vt from"node:fs/promises";import pf from"node:os";import jt from"node:path";import{execFile as sx}from"node:child_process";import{promisify as ax}from"node:util";import{createHash as ex,randomBytes as nx,timingSafeEqual as rx}from"node:crypto";var Ws="BOSTRAT_HOOK_TOKEN",qs="x-bostrat-hook";function uf(){return nx(32).toString("hex")}function go(t){let e=typeof t=="string"?t.trim():"";return e?ex("sha256").update(e).digest("hex"):null}function df(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:rx(Buffer.from(t),Buffer.from(e))}var oc=new Map,sc=new Map,ox=6e4;function Ks(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(oc.set(t,(oc.get(t)||0)+1),sc.has(t)&&n-sc.get(t)<ox)return!1;sc.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=${oc.get(t)}${o?` ${o}`:""}`),!0}async function ff(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 Gs(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function ix(){return process.env.CLAUDE_CONFIG_DIR?jt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):jt.join(pf.homedir(),".claude.json")}function cx(){return process.env.CLAUDE_CONFIG_DIR?jt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):jt.join(pf.homedir(),".claude","settings.json")}async function tn(t){if(!t||typeof t!="string")return;let e=jt.resolve(t),n=ix(),r=cx(),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(jt.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}: $${Ws}" --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(jt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(i,null,2)),await lx(e)}async function lx(t){try{let e=jt.resolve(t),n=jt.join(e,".bostrat","memory","claude"),r=jt.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(jt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Vt.mkdir(n,{recursive:!0}),await Vt.mkdir(jt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(o,null,2)),await ux(e,["/.claude/settings.local.json"])}catch{}}async function ux(t,e){try{let{stdout:n}=await ax(sx)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=jt.resolve(t,n.trim()),o=await Vt.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
2
- `).includes(a));if(!s.length)return;await Vt.mkdir(jt.dirname(r),{recursive:!0}),await Vt.appendFile(r,(o&&!o.endsWith(`
1
+ var rx=Object.defineProperty;var ff=(t,e)=>{for(var n in e)rx(t,n,{get:e[n],enumerable:!0})};import ft from"node:path";import Kt from"node:fs";import NN from"node:readline";import{fileURLToPath as PN}from"node:url";import Ee from"express";import DN from"node:http";import BN from"node:https";import lf from"node:os";import{WebSocketServer as jN}from"ws";import{execFile as Av}from"node:child_process";import{promisify as _v}from"node:util";import la from"node:fs";import{randomUUID as Ev}from"node:crypto";import Gt from"node:fs/promises";import wf from"node:os";import Bt from"node:path";import{execFile as cx}from"node:child_process";import{promisify as lx}from"node:util";import{createHash as ox,randomBytes as sx,timingSafeEqual as ax}from"node:crypto";var Vs="BOSTRAT_HOOK_TOKEN",zs="x-bostrat-hook";function pf(){return sx(32).toString("hex")}function xo(t){let e=typeof t=="string"?t.trim():"";return e?ox("sha256").update(e).digest("hex"):null}function mf(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:ax(Buffer.from(t),Buffer.from(e))}var lc=new Map,uc=new Map,ix=6e4;function Js(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(lc.set(t,(lc.get(t)||0)+1),uc.has(t)&&n-uc.get(t)<ix)return!1;uc.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=${lc.get(t)}${o?` ${o}`:""}`),!0}async function hf(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 Xs(){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?Bt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Bt.join(wf.homedir(),".claude.json")}function dx(){return process.env.CLAUDE_CONFIG_DIR?Bt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Bt.join(wf.homedir(),".claude","settings.json")}async function en(t){if(!t||typeof t!="string")return;let e=Bt.resolve(t),n=ux(),r=dx(),o={};try{o=JSON.parse(await Gt.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 Gt.mkdir(Bt.dirname(n),{recursive:!0}),await Gt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Gt.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 "${zs}: $${Vs}" --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 Gt.mkdir(Bt.dirname(r),{recursive:!0}),await Gt.writeFile(r,JSON.stringify(i,null,2)),await fx(e)}async function fx(t){try{let e=Bt.resolve(t),n=Bt.join(e,".bostrat","memory","claude"),r=Bt.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await Gt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Bt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Gt.mkdir(n,{recursive:!0}),await Gt.mkdir(Bt.dirname(r),{recursive:!0}),await Gt.writeFile(r,JSON.stringify(o,null,2)),await px(e,["/.claude/settings.local.json"])}catch{}}async function px(t,e){try{let{stdout:n}=await lx(cx)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Bt.resolve(t,n.trim()),o=await Gt.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
2
+ `).includes(a));if(!s.length)return;await Gt.mkdir(Bt.dirname(r),{recursive:!0}),await Gt.appendFile(r,(o&&!o.endsWith(`
3
3
  `)?`
4
4
  `:"")+s.join(`
5
5
  `)+`
6
- `)}catch{}}import ac from"node:fs/promises";import dx from"node:os";import Vs from"node:path";function fx(){let t=process.env.CODEX_HOME||Vs.join(dx.homedir(),".codex");return Vs.join(t,"config.toml")}function mf(t){return`[projects."${t}"]`}function px(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 dr(t){if(!t||typeof t!="string")return;let e=Vs.resolve(t),n=fx(),r="";try{r=await ac.readFile(n,"utf8")}catch{}if(px(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 dc from"node:fs/promises";import mx from"node:os";import Ys from"node:path";function hx(){let t=process.env.CODEX_HOME||Ys.join(mx.homedir(),".codex");return Ys.join(t,"config.toml")}function gf(t){return`[projects."${t}"]`}function wx(t,e){let n=gf(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 mr(t){if(!t||typeof t!="string")return;let e=Ys.resolve(t),n=hx(),r="";try{r=await dc.readFile(n,"utf8")}catch{}if(wx(r,e))return;let o=gf(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 ac.mkdir(Vs.dirname(n),{recursive:!0}),await ac.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import hf from"node:fs/promises";import mx from"node:os";import yo from"node:path";function hx(t){let n=yo.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||yo.join(mx.homedir(),".cursor");return yo.join(r,"projects",n)}async function fr(t){if(!t||typeof t!="string")return;let e=yo.resolve(t),n=hx(e),r=yo.join(n,".workspace-trusted");await hf.mkdir(n,{recursive:!0}),await hf.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
- `,"utf8")}import j from"node:fs/promises";import Hx from"node:os";import Z from"node:path";import{execFile as Wx}from"node:child_process";import{promisify as qx}from"node:util";import _x from"node:fs";import gf from"node:fs/promises";import ko from"node:path";import Ex from"node:os";import{fileURLToPath as Ix}from"node:url";import{execFile as Rx}from"node:child_process";import{promisify as $x}from"node:util";import cc from"node:fs/promises";import en from"node:path";import{execFile as wx}from"node:child_process";import{promisify as gx}from"node:util";var yx=gx(wx),wf=process.env.HOME||"/home/globo",bx=en.join(process.env.BOSTRAT_STATE_DIR||en.join(wf,".bostrat"),"repos"),kx=[...(process.env.BOSTRAT_REPOS_ROOT||en.join(wf,"Documents")).split(","),bx].map(t=>t.trim()).filter(Boolean),xx=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,Sx=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function vx(t){try{return(await cc.stat(en.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function Tx(t){let e=i=>cc.readFile(en.join(t,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await e("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await e(`refs/heads/${i}`)||(o??=await e("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await e("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function Re(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await vx(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:en.basename(o),branch:await Tx(o)});return}if(s>=xx)return;let i;try{i=await cc.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||Sx.has(c.name)||await e(en.join(o,c.name),s+1)}for(let o of kx)await e(en.resolve(o),0);let n=new Set,r=[];for(let o of t.values()){let s=(await ot(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 Ax(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var ic=new Map;async function ot(t){if(!t)return null;if(ic.has(t))return ic.get(t);let e=null;try{let{stdout:n}=await yx("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=Ax(n)}catch{e=null}return ic.set(t,e),e}async function bo(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await Re())if((await ot(n.path))?.toLowerCase()===e)return n.path;return null}var yf=$x(Rx),Ox=process.env.BOSTRAT_STATE_DIR||ko.join(process.env.HOME||Ex.tmpdir(),".bostrat"),Mx=ko.join(Ox,"repos"),bf=/^[^/\s]+\/[^/\s]+$/,Cx=5*6e4,Nx=6e4,Px=10*6e4,kf=fetch,lc=new Map,Nn=null;function te(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 zs(t=process.env){return!!(te(t)&&t.BOSTRAT_PLANE_TOKEN)}var xf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),pr=(t,e)=>Object.assign(new Error(t),{status:e});async function Js(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!bf.test(n))throw pr("repo must be owner/name",400);let r=lc.get(n);if(r&&r.expiresAt-Date.now()>Cx)return r;if(!zs(e))throw pr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await kf(`${te(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:xf(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw pr(`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 pr(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 lc.set(n,a),a}async function xo({env:t=process.env,force:e=!1}={}){if(!zs(t))return{configured:!1,repos:[],username:null};if(!e&&Nn&&Date.now()-Nn.at<Nx)return Nn.data;try{let n=await kf(`${te(t)}/v1/github/repos`,{headers:xf(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Nn={at:Date.now(),data:r},r}catch(n){return Nn?Nn.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Sf(){lc.clear(),Nn=null}async function de({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await ot(t):null);if(!r)return{};let{token:o}=await Js(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function uc({env:t=process.env}={}){return(await xo({env:t})).username??null}var Dx=()=>Ix(new URL("../bin/bostrat-git-credential",import.meta.url)),Bx=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function jx(t){let[e,n]=String(t).split("/");return ko.join(Mx,e,n)}async function dc(t,{env:e=process.env}={}){let n=(...r)=>yf("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Dx()}" ${Bx(e)}`),await n("config","credential.useHttpPath","true")}async function fc(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!bf.test(String(t||"")))throw pr(`bad repo slug "${t}"`,400);let r=jx(t);if(_x.existsSync(ko.join(r,".git")))return r;let{token:o}=await Js(t,{env:e});await gf.mkdir(ko.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await yf("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:Px,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await gf.rm(r,{recursive:!0,force:!0}).catch(()=>{}),pr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await dc(r,{env:e}),r}var Lx=6e4,Fx=5e3,pc=new Map,Ux=(...t)=>fetch(...t);function vf(t=process.env){return!!(te(t)&&t.BOSTRAT_PLANE_TOKEN)}var Tf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Af(t,e,n){let r=te(n),o=new AbortController,s=setTimeout(()=>o.abort(),Fx);try{return await Ux(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function _f(t,{env:e=process.env,force:n=!1}={}){if(!vf(e))return{facts:[],source:"unconfigured"};let r=pc.get(t);if(!n&&r&&Date.now()-r.at<Lx)return{facts:r.facts,source:"cache"};try{let o=await Af(`/v1/memory/${encodeURIComponent(t)}`,{headers:Tf(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return pc.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 Xs(t,e,{env:n=process.env}={}){if(!vf(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await Af(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Tf(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 pc.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 Kx=qx(Wx),mc=process.env.BOSTRAT_STATE_DIR||Z.join(process.env.HOME||Hx.tmpdir(),".bostrat"),Ef=200,Gx=25*1024;function Ys(){return Z.join(mc,"memory","repos")}var $e="operator";function Pn(t){return t===$e?Z.join(mc,"memory",$e):Z.join(Ys(),t)}var Of=".tombstones.json";async function So(t){try{return JSON.parse(await j.readFile(Z.join(t,Of),"utf8"))}catch{return{}}}async function hc(t,e){let n=await So(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await j.mkdir(t,{recursive:!0}),await j.writeFile(Z.join(t,Of),JSON.stringify(n))}function If(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 Rf(t){try{let{stdout:e}=await Kx("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function wc(t){if(!t)return[];let e=If(await Rf(t));if(e)return[e];let n=[],r=[];try{r=await j.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=If(await Rf(Z.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Zs(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 nn(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function fe(t,e=null){let n=e??await j.readFile(t,"utf8").catch(()=>""),{fm:r}=Zs(n),o=nn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await j.stat(t)).mtimeMs}catch{return 0}}function Mf(t,e){let n=nn(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===$e){let o=nn(t,"trust");if(!o||/^operator/i.test(o))return $e}return e}var mr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function gc(t,e,n,r=new Map){let o=(t||"").split(`
13
+ `:"")}try{await dc.mkdir(Ys.dirname(n),{recursive:!0}),await dc.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import yf from"node:fs/promises";import gx from"node:os";import vo from"node:path";function yx(t){let n=vo.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||vo.join(gx.homedir(),".cursor");return vo.join(r,"projects",n)}async function hr(t){if(!t||typeof t!="string")return;let e=vo.resolve(t),n=yx(e),r=vo.join(n,".workspace-trusted");await yf.mkdir(n,{recursive:!0}),await yf.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
+ `,"utf8")}import j from"node:fs/promises";import Kx from"node:os";import X from"node:path";import{execFile as Gx}from"node:child_process";import{promisify as Vx}from"node:util";import Rx from"node:fs";import kf from"node:fs/promises";import To from"node:path";import $x from"node:os";import{fileURLToPath as Ox}from"node:url";import{execFile as Mx}from"node:child_process";import{promisify as Cx}from"node:util";import pc from"node:fs/promises";import nn from"node:path";import{execFile as bx}from"node:child_process";import{promisify as kx}from"node:util";var xx=kx(bx),bf=process.env.HOME||"/home/globo",vx=nn.join(process.env.BOSTRAT_STATE_DIR||nn.join(bf,".bostrat"),"repos"),Sx=[...(process.env.BOSTRAT_REPOS_ROOT||nn.join(bf,"Documents")).split(","),vx].map(t=>t.trim()).filter(Boolean),Tx=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,Ax=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function _x(t){try{return(await pc.stat(nn.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function Ex(t){let e=i=>pc.readFile(nn.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 Ie(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await _x(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:nn.basename(o),branch:await Ex(o)});return}if(s>=Tx)return;let i;try{i=await pc.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||Ax.has(c.name)||await e(nn.join(o,c.name),s+1)}for(let o of Sx)await e(nn.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 Ix(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var fc=new Map;async function et(t){if(!t)return null;if(fc.has(t))return fc.get(t);let e=null;try{let{stdout:n}=await xx("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=Ix(n)}catch{e=null}return fc.set(t,e),e}async function So(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await Ie())if((await et(n.path))?.toLowerCase()===e)return n.path;return null}var xf=Cx(Mx),Nx=process.env.BOSTRAT_STATE_DIR||To.join(process.env.HOME||$x.tmpdir(),".bostrat"),Px=To.join(Nx,"repos"),vf=/^[^/\s]+\/[^/\s]+$/,Dx=5*6e4,Bx=6e4,jx=10*6e4,Sf=fetch,mc=new Map,Bn=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 Zs(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Tf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),wr=(t,e)=>Object.assign(new Error(t),{status:e});async function Qs(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!vf.test(n))throw wr("repo must be owner/name",400);let r=mc.get(n);if(r&&r.expiresAt-Date.now()>Dx)return r;if(!Zs(e))throw wr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Sf(`${Qt(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Tf(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw wr(`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 wr(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 mc.set(n,a),a}async function Ao({env:t=process.env,force:e=!1}={}){if(!Zs(t))return{configured:!1,repos:[],username:null};if(!e&&Bn&&Date.now()-Bn.at<Bx)return Bn.data;try{let n=await Sf(`${Qt(t)}/v1/github/repos`,{headers:Tf(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Bn={at:Date.now(),data:r},r}catch(n){return Bn?Bn.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Af(){mc.clear(),Bn=null}async function de({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 Qs(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function hc({env:t=process.env}={}){return(await Ao({env:t})).username??null}var Lx=()=>Ox(new URL("../bin/bostrat-git-credential",import.meta.url)),Fx=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Ux(t){let[e,n]=String(t).split("/");return To.join(Px,e,n)}async function wc(t,{env:e=process.env}={}){let n=(...r)=>xf("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Lx()}" ${Fx(e)}`),await n("config","credential.useHttpPath","true")}async function gc(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!vf.test(String(t||"")))throw wr(`bad repo slug "${t}"`,400);let r=Ux(t);if(Rx.existsSync(To.join(r,".git")))return r;let{token:o}=await Qs(t,{env:e});await kf.mkdir(To.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await xf("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:jx,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await kf.rm(r,{recursive:!0,force:!0}).catch(()=>{}),wr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await wc(r,{env:e}),r}var Hx=6e4,Wx=5e3,yc=new Map,qx=(...t)=>fetch(...t);function _f(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Ef=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function If(t,e,n){let r=Qt(n),o=new AbortController,s=setTimeout(()=>o.abort(),Wx);try{return await qx(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function Rf(t,{env:e=process.env,force:n=!1}={}){if(!_f(e))return{facts:[],source:"unconfigured"};let r=yc.get(t);if(!n&&r&&Date.now()-r.at<Hx)return{facts:r.facts,source:"cache"};try{let o=await If(`/v1/memory/${encodeURIComponent(t)}`,{headers:Ef(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return yc.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 ta(t,e,{env:n=process.env}={}){if(!_f(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await If(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Ef(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 yc.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 zx=Vx(Gx),bc=process.env.BOSTRAT_STATE_DIR||X.join(process.env.HOME||Kx.tmpdir(),".bostrat"),$f=200,Jx=25*1024;function ea(){return X.join(bc,"memory","repos")}var Re="operator";function jn(t){return t===Re?X.join(bc,"memory",Re):X.join(ea(),t)}var Nf=".tombstones.json";async function _o(t){try{return JSON.parse(await j.readFile(X.join(t,Nf),"utf8"))}catch{return{}}}async function kc(t,e){let n=await _o(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await j.mkdir(t,{recursive:!0}),await j.writeFile(X.join(t,Nf),JSON.stringify(n))}function Of(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 Mf(t){try{let{stdout:e}=await zx("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function xc(t){if(!t)return[];let e=Of(await Mf(t));if(e)return[e];let n=[],r=[];try{r=await j.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Of(await Mf(X.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function na(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 rn(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function fe(t,e=null){let n=e??await j.readFile(t,"utf8").catch(()=>""),{fm:r}=na(n),o=rn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await j.stat(t)).mtimeMs}catch{return 0}}function Pf(t,e){let n=rn(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Re){let o=rn(t,"trust");if(!o||/^operator/i.test(o))return Re}return e}var gr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function vc(t,e,n,r=new Map){let o=(t||"").split(`
15
15
  `),s=new Map;for(let c of(e||"").split(`
16
- `)){let l=mr(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=mr(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
16
+ `)){let l=gr(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=gr(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 Cf(t,e){let n=nn(e,"name")||t.replace(/\.md$/,""),r=nn(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function vo(t){let e=[];try{e=await j.readdir(t,{withFileTypes:!0})}catch{return[]}return e.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function Nf(t,e){let n=Z.join(t,".bostrat","memory"),r=await vo(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=new Map,s=new Map,a=0,i=new Map;for(let l of r){let u=Z.join(n,l),f;try{f=await j.readFile(u,"utf8")}catch{continue}let{fm:d}=Zs(f),p=Mf(d,e);if(!p)continue;let m=Pn(p),w=Z.join(m,l),y=await fe(u,f);if(i.has(m)||i.set(m,await So(m)),(i.get(m)[l]||0)>=y)continue;let b=await fe(w).catch(()=>0);if(await j.access(w).then(()=>!0).catch(()=>!1)&&b>=y)continue;await j.mkdir(m,{recursive:!0}),await j.writeFile(w,f),a++;let E=o.get(m);E||o.set(m,E={won:new Set,generated:new Map}),E.won.add(l),E.generated.set(l,Cf(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await j.readFile(Z.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=Z.join(l,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,gc(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Vx(){let t=Z.join(mc,"workspaces"),e=[];try{e=await j.readdir(t,{withFileTypes:!0})}catch{return{harvested:0}}let n=0,r=new Map;for(let o of e){if(!o.isDirectory())continue;let s=Z.join(t,o.name);if(!await j.access(Z.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await wc(s),c=await Nf(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 zx=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,Jx=t=>{let e=new Map;for(let n of(t||"").split(`
20
- `)){let r=mr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function Xx(t,e){let n=Pn(t),r=await So(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!zx.test(l)||l==="MEMORY.md")continue;let u=Z.join(n,l),f=await j.access(u).then(()=>!0).catch(()=>!1),d=f?await fe(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await j.rm(u,{force:!0}),await hc(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await j.mkdir(n,{recursive:!0}),await j.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=Z.join(n,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=gc(l,[...s.values()].join(`
19
+ `)}function Df(t,e){let n=rn(e,"name")||t.replace(/\.md$/,""),r=rn(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function Eo(t){let e=[];try{e=await j.readdir(t,{withFileTypes:!0})}catch{return[]}return e.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function Bf(t,e){let n=X.join(t,".bostrat","memory"),r=await Eo(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=new Map,s=new Map,a=0,i=new Map;for(let l of r){let u=X.join(n,l),f;try{f=await j.readFile(u,"utf8")}catch{continue}let{fm:d}=na(f),p=Pf(d,e);if(!p)continue;let m=jn(p),w=X.join(m,l),y=await fe(u,f);if(i.has(m)||i.set(m,await _o(m)),(i.get(m)[l]||0)>=y)continue;let k=await fe(w).catch(()=>0);if(await j.access(w).then(()=>!0).catch(()=>!1)&&k>=y)continue;await j.mkdir(m,{recursive:!0}),await j.writeFile(w,f),a++;let E=o.get(m);E||o.set(m,E={won:new Set,generated:new Map}),E.won.add(l),E.generated.set(l,Df(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await j.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=X.join(l,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,vc(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Xx(){let t=X.join(bc,"workspaces"),e=[];try{e=await j.readdir(t,{withFileTypes:!0})}catch{return{harvested:0}}let n=0,r=new Map;for(let o of e){if(!o.isDirectory())continue;let s=X.join(t,o.name);if(!await j.access(X.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await xc(s),c=await Bf(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 Yx=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,Zx=t=>{let e=new Map;for(let n of(t||"").split(`
20
+ `)){let r=gr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function Qx(t,e){let n=jn(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(!Yx.test(l)||l==="MEMORY.md")continue;let u=X.join(n,l),f=await j.access(u).then(()=>!0).catch(()=>!1),d=f?await fe(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await j.rm(u,{force:!0}),await kc(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await j.mkdir(n,{recursive:!0}),await j.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=X.join(n,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=vc(l,[...s.values()].join(`
21
21
  `),o);if(a.length){let f=new Set(a);u=u.split(`
22
- `).filter(d=>!f.has(mr(d))).join(`
23
- `)}await j.mkdir(n,{recursive:!0}),await j.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function yc(t,e=null){let n=Pn(t),r=e?.length?e:await vo(n);if(!r.length)return{pushed:0,source:"empty"};let o=Jx(await j.readFile(Z.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=Z.join(n,a),c;try{c=await j.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await fe(i,c),indexLine:o.get(a)??null})}return Xs(t,s)}async function Yx(t){let{facts:e,source:n}=await _f(t);e.length?await Xx(t,e):n==="plane"&&await yc(t)}function Zx(t){let e=t.split(`
24
- `);e.length>Ef&&(e=e.slice(0,Ef));let n=e.join(`
25
- `);for(;Buffer.byteLength(n,"utf8")>Gx&&e.length;)e.pop(),n=e.join(`
26
- `);return n}var Qx=`# Track memory
22
+ `).filter(d=>!f.has(gr(d))).join(`
23
+ `)}await j.mkdir(n,{recursive:!0}),await j.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Sc(t,e=null){let n=jn(t),r=e?.length?e:await Eo(n);if(!r.length)return{pushed:0,source:"empty"};let o=Zx(await j.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=X.join(n,a),c;try{c=await j.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await fe(i,c),indexLine:o.get(a)??null})}return ta(t,s)}async function tv(t){let{facts:e,source:n}=await Rf(t);e.length?await Qx(t,e):n==="plane"&&await Sc(t)}function ev(t){let e=t.split(`
24
+ `);e.length>$f&&(e=e.slice(0,$f));let n=e.join(`
25
+ `);for(;Buffer.byteLength(n,"utf8")>Jx&&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 tS(t,e){if(!t||!e?.length)return{materialized:!1,reason:"no-repo-key"};let n=Z.join(t,".bostrat","memory"),r=0,o=0,s=[];for(let u of e){let f=Pn(u),d=await So(f);for(let[y,b]of Object.entries(d)){let S=Z.join(n,y);await j.access(S).then(()=>!0).catch(()=>!1)&&await fe(S)<=b&&(await j.rm(S,{force:!0}),o++)}let p=await vo(f);if(!p.length)continue;await j.mkdir(n,{recursive:!0});for(let y of p){let b=Z.join(f,y),S=Z.join(n,y),E=await fe(b),O=await fe(S).catch(()=>0);await j.access(S).then(()=>!0).catch(()=>!1)&&O>=E||(await j.copyFile(b,S),r++)}let m=await j.readFile(Z.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===$e?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${w}
31
+ `;async function rv(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=jn(u),d=await _o(f);for(let[y,k]of Object.entries(d)){let v=X.join(n,y);await j.access(v).then(()=>!0).catch(()=>!1)&&await fe(v)<=k&&(await j.rm(v,{force:!0}),o++)}let p=await Eo(f);if(!p.length)continue;await j.mkdir(n,{recursive:!0});for(let y of p){let k=X.join(f,y),v=X.join(n,y),E=await fe(k),$=await fe(v).catch(()=>0);await j.access(v).then(()=>!0).catch(()=>!1)&&$>=E||(await j.copyFile(k,v),r++)}let m=await j.readFile(X.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===Re?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${w}
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=Zx(i);await j.mkdir(n,{recursive:!0}),await j.writeFile(Z.join(n,"MEMORY.md"),c);let l=Z.join(n,"claude");return await j.mkdir(l,{recursive:!0}),await j.writeFile(Z.join(l,"MEMORY.md"),Qx,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var $f=0,eS=3e4;async function Pf(t){try{let e=await wc(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,$e];if(Date.now()-$f>eS){$f=Date.now(),await nS();let{changedByKey:r}=await Vx();for(let[o,s]of r)await yc(o,[...s])}for(let r of n)await Yx(r);return await tS(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function nS(){let t=0,e=[];try{e=await j.readdir(Ys(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=Z.join(Ys(),n.name),o=Pn($e),s=new Set,a=new Map;for(let p of await vo(r)){let m=Z.join(r,p),w;try{w=await j.readFile(m,"utf8")}catch{continue}let{fm:y}=Zs(w);if(Mf(y,n.name)!==$e)continue;let b=Z.join(o,p),S=await fe(b).catch(()=>0),E=await fe(m,w);await j.mkdir(o,{recursive:!0}),E>S&&await j.writeFile(b,w),await j.rm(m,{force:!0}),s.add(p),a.set(p,Cf(p,y)),t++}if(!s.size)continue;let i=Date.now();await hc(r,[...s].map(p=>[p,i])),await Xs(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=Z.join(r,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=l.split(`
37
- `).filter(p=>!s.has(mr(p))).join(`
38
- `);await j.writeFile(c,u).catch(()=>{});let f=Z.join(o,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,gc(d,l,s,a))}return{moved:t}}async function Df(){let t=[],e=[$e];try{for(let n of await j.readdir(Ys(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=Pn(n),o=await vo(r),s=await So(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await j.readFile(Z.join(r,i),"utf8").catch(()=>""),{fm:l}=Zs(c);a.push({file:i,name:nn(l,"name")||i.replace(/\.md$/,""),description:nn(l,"description")||"",scope:nn(l,"scope")||null,modified:await fe(Z.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function Bf(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=Pn(t),r=Z.join(n,e);if(!await j.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await j.rm(r,{force:!0}),await hc(n,[[e,s]]);let a=Z.join(n,"MEMORY.md"),i=await j.readFile(a,"utf8").catch(()=>"");await j.writeFile(a,i.split(`
39
- `).filter(l=>mr(l)!==e).join(`
40
- `)).catch(()=>{});let c=await Xs(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function jf(t){try{let e=await wc(t),{harvested:n,changedByKey:r}=await Nf(t,e[0]||null);for(let[o,s]of r)await yc(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import vc from"node:fs/promises";import zf from"node:path";import Wf from"node:fs/promises";import qf from"node:os";import Kf from"node:path";import bc from"node:fs/promises";import rS from"node:path";var oS=0;async function Lt(t,e,{mode:n}={}){await bc.mkdir(rS.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++oS}.tmp`;await bc.writeFile(r,JSON.stringify(e,null,2)+`
41
- `,n!=null?{mode:n}:void 0),await bc.rename(r,t)}var sS=1e4,aS=3e4,Lf=2e3,iS=5,Ff=100,cS=60*6e4,lS=()=>process.env.BOSTRAT_STATE_DIR||Kf.join(process.env.HOME||qf.tmpdir(),".bostrat"),Gf=()=>Kf.join(lS(),"content-outbox.json"),uS=()=>process.env.BOSTRAT_NODE_ID||qf.hostname(),dS=(...t)=>fetch(...t),fS=aS,pS=cS;function wt(t=process.env){return!!(te(t)&&t.BOSTRAT_PLANE_TOKEN)}var Ao=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function _o(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),sS);try{return await dS(`${te(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var ta=null,kc=null,Qs=null,To=null;async function Vf(){return ta||(kc||=Wf.readFile(Gf(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>ta={ops:Array.isArray(t?.ops)?t.ops:[],parked:Array.isArray(t?.parked)?t.parked:[]}).finally(()=>kc=null),kc)}async function ea(){await Lt(Gf(),ta).catch(()=>{})}function Uf(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function Sc(t){let e=await Vf(),n=Uf(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=>Uf(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>Lf&&(e.ops=e.ops.slice(-Lf)),await ea()}function Hf(){Qs||(Qs=setTimeout(()=>{Qs=null,hr().catch(()=>{})},fS),Qs.unref?.())}function xc(t){let e=new Error(`plane ${t}`);return e.refused=!0,e}async function mS(t,e=process.env){if(t.op==="record"){let n=await _o(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...Ao(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:uS()}]})},e);if(!n.ok)throw xc(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 Wf.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await _o(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...Ao(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw xc(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 _o(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:Ao(e)},e);if(!n.ok)throw xc(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function hr({env:t=process.env,log:e=console,now:n=Date.now()}={}){return wt(t)?To||(To=(async()=>{let r=await Vf();hS(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await mS(a,t)}catch(c){if(!c?.refused)return Hf(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<iS)return Hf(),await ea(),{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>Ff&&(r.parked=r.parked.slice(-Ff)),s++,await ea();continue}i.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await ea()}return{flushed:o,parked:s,pending:0}})().finally(()=>{To=null}),To):{flushed:0,source:"unconfigured"}}function hS(t,e){if(!t.parked.length||t.ops.length)return;let n=t.parked.filter(r=>e-(r.parkedAt||0)>=pS);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 Dt(t,e,{trackId:n=null,env:r=process.env}={}){return wt(r)?(await Sc({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),hr({env:r})):{queued:!1,source:"unconfigured"}}async function rn(t,e,n,r,o,{env:s=process.env}={}){return wt(s)?(await Sc({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),hr({env:s})):{queued:!1,source:"unconfigured"}}async function re(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return wt(r)?(await Sc({op:"delete",family:t,id:e,deletedAt:n}),hr({env:r})):{queued:!1,source:"unconfigured"}}async function Ot(t,{env:e=process.env}={}){if(!wt(e))return{records:[],source:"unconfigured"};try{let n=await _o(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:Ao(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 na(t,e,n,r,{env:o=process.env}={}){if(!wt(o))return null;try{let s=await _o(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ao(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="png"?a:a.toString("utf8")}catch{return null}}var wS=".bostrat/handoff",gS="latest.md",yS=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null;async function Jf(t,e,{box:n=null,agent:r=null}={}){if(!t||!wt())return{pushed:!1};try{let o=Date.now();return await Dt("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await rn("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function Xf(t){try{let e=yS(t);if(!e||!wt())return{restored:!1};let n=zf.join(t,wS,gS);if(await vc.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await Ot("checkpoints");if(o!=="plane")return{restored:!1};let s=r.find(i=>!i.deletedAt&&i.data?.id===e);if(!s?.data?.v)return{restored:!1};let a=await na("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await vc.mkdir(zf.dirname(n),{recursive:!0}),await vc.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import Eo from"node:fs/promises";import bS from"node:os";import Zf from"node:path";var kS=[/[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 Io(t){let e=String(t??"");for(let n of kS)e=e.replace(n,"\u2039redacted\u203A");return e}var Tc=process.env.BOSTRAT_DEBUG_DIR||Zf.join(bS.homedir(),".bostrat","debug"),wr=Zf.join(Tc,"handoff.jsonl"),Yf=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function ra(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function xS(t){try{await Eo.mkdir(Tc,{recursive:!0}),await Eo.appendFile(wr,`${JSON.stringify(t)}
42
- `,"utf8");let n=(await Eo.readFile(wr,"utf8")).split(`
43
- `).filter(Boolean);n.length>Yf&&await Eo.writeFile(wr,`${n.slice(-Yf).join(`
36
+ `,c=ev(i);await j.mkdir(n,{recursive:!0}),await j.writeFile(X.join(n,"MEMORY.md"),c);let l=X.join(n,"claude");return await j.mkdir(l,{recursive:!0}),await j.writeFile(X.join(l,"MEMORY.md"),nv,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var Cf=0,ov=3e4;async function jf(t){try{let e=await xc(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Re];if(Date.now()-Cf>ov){Cf=Date.now(),await sv();let{changedByKey:r}=await Xx();for(let[o,s]of r)await Sc(o,[...s])}for(let r of n)await tv(r);return await rv(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function sv(){let t=0,e=[];try{e=await j.readdir(ea(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=X.join(ea(),n.name),o=jn(Re),s=new Set,a=new Map;for(let p of await Eo(r)){let m=X.join(r,p),w;try{w=await j.readFile(m,"utf8")}catch{continue}let{fm:y}=na(w);if(Pf(y,n.name)!==Re)continue;let k=X.join(o,p),v=await fe(k).catch(()=>0),E=await fe(m,w);await j.mkdir(o,{recursive:!0}),E>v&&await j.writeFile(k,w),await j.rm(m,{force:!0}),s.add(p),a.set(p,Df(p,y)),t++}if(!s.size)continue;let i=Date.now();await kc(r,[...s].map(p=>[p,i])),await ta(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=X.join(r,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=l.split(`
37
+ `).filter(p=>!s.has(gr(p))).join(`
38
+ `);await j.writeFile(c,u).catch(()=>{});let f=X.join(o,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,vc(d,l,s,a))}return{moved:t}}async function Lf(){let t=[],e=[Re];try{for(let n of await j.readdir(ea(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=jn(n),o=await Eo(r),s=await _o(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await j.readFile(X.join(r,i),"utf8").catch(()=>""),{fm:l}=na(c);a.push({file:i,name:rn(l,"name")||i.replace(/\.md$/,""),description:rn(l,"description")||"",scope:rn(l,"scope")||null,modified:await fe(X.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function Ff(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=jn(t),r=X.join(n,e);if(!await j.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await j.rm(r,{force:!0}),await kc(n,[[e,s]]);let a=X.join(n,"MEMORY.md"),i=await j.readFile(a,"utf8").catch(()=>"");await j.writeFile(a,i.split(`
39
+ `).filter(l=>gr(l)!==e).join(`
40
+ `)).catch(()=>{});let c=await ta(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function Uf(t){try{let e=await xc(t),{harvested:n,changedByKey:r}=await Bf(t,e[0]||null);for(let[o,s]of r)await Sc(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import Ic from"node:fs/promises";import Yf from"node:path";import Gf from"node:fs/promises";import Vf from"node:os";import zf from"node:path";import Tc from"node:fs/promises";import av from"node:path";var iv=0;async function jt(t,e,{mode:n}={}){await Tc.mkdir(av.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++iv}.tmp`;await Tc.writeFile(r,JSON.stringify(e,null,2)+`
41
+ `,n!=null?{mode:n}:void 0),await Tc.rename(r,t)}var cv=1e4,lv=3e4,Hf=2e3,uv=5,Wf=100,dv=60*6e4,fv=()=>process.env.BOSTRAT_STATE_DIR||zf.join(process.env.HOME||Vf.tmpdir(),".bostrat"),Jf=()=>zf.join(fv(),"content-outbox.json"),pv=()=>process.env.BOSTRAT_NODE_ID||Vf.hostname(),mv=(...t)=>fetch(...t),hv=lv,wv=dv;function ht(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Ro=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function $o(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),cv);try{return await mv(`${Qt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var oa=null,Ac=null,ra=null,Io=null;async function Xf(){return oa||(Ac||=Gf.readFile(Jf(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>oa={ops:Array.isArray(t?.ops)?t.ops:[],parked:Array.isArray(t?.parked)?t.parked:[]}).finally(()=>Ac=null),Ac)}async function sa(){await jt(Jf(),oa).catch(()=>{})}function qf(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function Ec(t){let e=await Xf(),n=qf(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=>qf(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>Hf&&(e.ops=e.ops.slice(-Hf)),await sa()}function Kf(){ra||(ra=setTimeout(()=>{ra=null,yr().catch(()=>{})},hv),ra.unref?.())}function _c(t){let e=new Error(`plane ${t}`);return e.refused=!0,e}async function gv(t,e=process.env){if(t.op==="record"){let n=await $o(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...Ro(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:pv()}]})},e);if(!n.ok)throw _c(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 Gf.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await $o(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...Ro(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw _c(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 $o(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:Ro(e)},e);if(!n.ok)throw _c(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function yr({env:t=process.env,log:e=console,now:n=Date.now()}={}){return ht(t)?Io||(Io=(async()=>{let r=await Xf();yv(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await gv(a,t)}catch(c){if(!c?.refused)return Kf(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<uv)return Kf(),await sa(),{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>Wf&&(r.parked=r.parked.slice(-Wf)),s++,await sa();continue}i.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await sa()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Io=null}),Io):{flushed:0,source:"unconfigured"}}function yv(t,e){if(!t.parked.length||t.ops.length)return;let n=t.parked.filter(r=>e-(r.parkedAt||0)>=wv);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 Nt(t,e,{trackId:n=null,env:r=process.env}={}){return ht(r)?(await Ec({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),yr({env:r})):{queued:!1,source:"unconfigured"}}async function on(t,e,n,r,o,{env:s=process.env}={}){return ht(s)?(await Ec({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),yr({env:s})):{queued:!1,source:"unconfigured"}}async function ne(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return ht(r)?(await Ec({op:"delete",family:t,id:e,deletedAt:n}),yr({env:r})):{queued:!1,source:"unconfigured"}}async function Rt(t,{env:e=process.env}={}){if(!ht(e))return{records:[],source:"unconfigured"};try{let n=await $o(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:Ro(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 aa(t,e,n,r,{env:o=process.env}={}){if(!ht(o))return null;try{let s=await $o(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ro(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="png"?a:a.toString("utf8")}catch{return null}}var bv=".bostrat/handoff",kv="latest.md",xv=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null;async function Zf(t,e,{box:n=null,agent:r=null}={}){if(!t||!ht())return{pushed:!1};try{let o=Date.now();return await Nt("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await on("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function Qf(t){try{let e=xv(t);if(!e||!ht())return{restored:!1};let n=Yf.join(t,bv,kv);if(await Ic.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await Rt("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 aa("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Ic.mkdir(Yf.dirname(n),{recursive:!0}),await Ic.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import Oo from"node:fs/promises";import vv from"node:os";import ep from"node:path";var Sv=[/[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 Mo(t){let e=String(t??"");for(let n of Sv)e=e.replace(n,"\u2039redacted\u203A");return e}var Rc=process.env.BOSTRAT_DEBUG_DIR||ep.join(vv.homedir(),".bostrat","debug"),br=ep.join(Rc,"handoff.jsonl"),tp=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function ia(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function Tv(t){try{await Oo.mkdir(Rc,{recursive:!0}),await Oo.appendFile(br,`${JSON.stringify(t)}
42
+ `,"utf8");let n=(await Oo.readFile(br,"utf8")).split(`
43
+ `).filter(Boolean);n.length>tp&&await Oo.writeFile(br,`${n.slice(-tp).join(`
44
44
  `)}
45
- `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${wr}: ${e?.message||e}`)}}async function G(t={}){let e={at:new Date().toISOString(),ts:Date.now(),...t},n=[e.flow||"handoff",e.phase||e.step||"?",e.trackId?`track=${e.trackId}`:"",e.sessionName?`box=${e.sessionName}`:"",e.error?`ERR=${e.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),e.sessionName&&ra())try{e.paneTail=Io((await mt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(ra()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await xS(e)}async function Qf({limit:t=80}={}){try{return(await Eo.readFile(wr,"utf8")).split(`
46
- `).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Io(o.paneTail)),o}catch{return{raw:Io(r)}}})}catch{return[]}}var tp={DEBUG_DIR:Tc,LOG_FILE:wr};var st=vS(SS);function br(t){return new Promise(e=>setTimeout(e,t))}function oa(t,e){if(!e)return t;let n=o=>typeof o=="string"?o.split(e).join("<redacted>"):o,r=new Error(n(t?.message)||"command failed");return t?.code!==void 0&&(r.code=t.code),t?.stderr!==void 0&&(r.stderr=n(t.stderr)),r}var M=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";function np(t){return`/tmp/tmux-${t}/default`}function AS({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=np(r);return e?o:n?`${n}/bostrat/tmux-default`:o}var ep=typeof process.getuid=="function"?process.getuid():0,zt=AS({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return sa.existsSync(np(ep))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:ep}),_S=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",_c=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",ES="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";function Ec(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${ES}=1`]}var Ic="bostrat-usage-probe",IS=new Set([Ic]);function Rc(t,e){let n=Dn(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function Dn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function on(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function RS(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:Dn(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function rp(t,{loginShell:e=!1,failOpen:n=!1}={}){let r=await op(t,{loginShell:e});return r===aa?!0:r===gr?!1:n}var aa="present",gr="absent",yr="unknown",Ac=5e3,$S=[250,1e3,3e3],OS=t=>new Promise(e=>setTimeout(e,t));async function op(t,{loginShell:e=!1,timeoutMs:n=Ac}={}){if(!t)return gr;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await st(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:n}),aa}catch(s){return s?.killed||s?.signal?yr:s?.code===1||s?.code===127?gr:yr}}async function ia(t,{retries:e=$S,sleep:n=OS,probe:r=op}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Ac});for(let a=0;s===yr&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:Ac*2});return s}var $c={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 N(...t){return["-S",zt,...t]}async function sn(){if(!sa.existsSync(zt))return!1;try{return await st(M,N("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function Ro(){if(await sn())return!0;try{sa.mkdirSync(zt.replace(/\/[^/]+$/,""),{recursive:!0}),await st(M,N("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await sn()}var MS=/^[A-Za-z0-9_.-]{1,64}$/;function bt(t){return typeof t=="string"&&MS.test(t)}async function Oe(){if(!sa.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 st(M,N("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=>!IS.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function rt(t){return(await Oe()).find(n=>n.name===t)||null}var CS=/^[A-Za-z0-9 ._↻-]+$/,NS=64;function PS(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=NS&&CS.test(e)}function Oc(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 st(M,N("list-sessions","-F","#{session_name}"));return e.split(`
48
- `).map(n=>n.trim()).includes(t)}catch{return!1}}var DS=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,BS=1e3,jS=/not found|Unknown command/i;function LS(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 FS(t,e){let n=Date.now()+DS(),r=LS(e),o=!1,s=0;for(;;){if(await br(BS),s+=1,!await sp(t))return;let a=await mt(t,{lines:15}),i=!o&&jS.test(a)&&!!r?.test(a);if(!i&&s>1&&!await kr(t))return;if(i){o=!0,await G({flow:"spawn",phase:"launch-retry",sessionName:t,agent:on(e),promptHead:e.slice(0,120)}).catch(()=>{}),await st(M,N("send-keys","-t",t,"C-u")),await st(M,N("send-keys","-t",t,"-l",e)),await st(M,N("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await G({flow:"spawn",phase:"launch-failed",sessionName:t,agent:on(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Bn(t,e=_S,n={}){if(!PS(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Oc(r);if(!bt(o))throw new Error("resulting tmux session name is invalid");if(!await Ro())throw new Error(`host tmux server not running on ${zt} and auto-start failed \u2014 start one: ${M} -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||_c,a=n.agent||on(s),i=!!n.resumeSessionId,c=n.resumeSessionId||TS(),l=i?s:Rc(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&Dn(s),d=n.firstRun===!0;d||(f&&await tn(e),a==="codex"&&await dr(e),a==="cursor"&&await fr(e),await Pf(e),await Xf(e));let p=f&&!d?Gs():null,m=uf(),w=["-e",`${Ws}=${m}`];p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&w.push(...Ec());try{await st(M,N("new-session","-d","-s",o,"-c",e,...w))}catch(y){throw oa(p?oa(y,p):y,m)}return await jn(o),await st(M,N("set-option","-t",o,"@bostrat-session","1")),await st(M,N("set-option","-t",o,"@bostrat-title",r)),await st(M,N("set-option","-t",o,"@bostrat-sid",c)),await st(M,N("set-option","-t",o,"@bostrat-agent",a)),await st(M,N("send-keys","-t",o,l,"Enter")),RS(l)&&FS(o,l).catch(()=>{}),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function pe(t){if(!bt(t))throw new Error("invalid session name");await st(M,N("kill-session","-t",t))}async function ap(t,e){if(bt(t))try{let n=e?N("set-option","-t",t,"@bostrat-sid",e):N("set-option","-u","-t",t,"@bostrat-sid");await st(M,n)}catch{}}async function mt(t,{lines:e=40}={}){if(!bt(t))return"";try{let n=N("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await st(M,n);return String(r||"")}catch{return""}}var US=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,HS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,_t=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,WS=Number(process.env.BOSTRAT_BOX_COLS)||120,qS=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function ip(t){if(!bt(t))return 0;try{let{stdout:e}=await st(M,N("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
49
- `).filter(Boolean).length}catch{return 0}}async function jn(t,{cols:e=WS,rows:n=qS}={}){if(!bt(t))return!0;if(await ip(t))return!1;try{let r=await cp(t);if(r.cols===e&&r.rows===n)return!0;await st(M,N("resize-window","-t",t,"-x",String(e),"-y",String(n))),await st(M,N("set-window-option","-t",t,"window-size","latest")),await br(80)}catch{}return!0}async function KS(t){if(!bt(t))return 0;try{let{stdout:e}=await st(M,N("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function kr(t){let e=await KS(t);if(!e)return!1;try{return await st("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function cp(t){if(!bt(t))return{cols:0,rows:0};try{let{stdout:e}=await st(M,N("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 ca(t,{cols:e=US,rows:n=HS}={}){if(bt(t)&&!await ip(t))try{let r=await cp(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await st(M,N("resize-window","-t",t,"-x",String(o),"-y",String(s))),await st(M,N("set-window-option","-t",t,"window-size","latest")),await br(80))}catch{}}async function an(t){return await ca(t),mt(t,{lines:_t})}var GS=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function vt(t,e,{settleMs:n=40,kind:r}={}){if(!bt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>GS.has(s));for(let s=0;s<o.length;s++)await st(M,N("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await br(n);return G({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function la(t,e,{kind:n}={}){if(!bt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await st(M,N("send-keys","-t",t,"-N",String(r),"BSpace")),G({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function ua(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!bt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await st(M,N("send-keys","-t",t,"-l",s)),n>0&&await br(n);G({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(!bt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await st(M,N("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await br(s),await st(M,N("send-keys","-t",t,"Enter"))}G({flow:"tmux",phase:"send",sessionName:t,kind:o||"text",textLen:typeof e=="string"?e.length:0,textHead:typeof e=="string"?e.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}import VS from"node-pty";async function up(t,e,n={}){if(!bt(e)){da(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await sn()){da(t,`host tmux server not running on ${zt}`),t.close(1011,"tmux server unavailable");return}if(!await rt(e)){da(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=lp(n.cols,120,dp),o=lp(n.rows,32,fp),s;try{s=VS.spawn(M,["-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){da(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Mc(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Mc(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"&&YS(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{}XS(e)})}var zS=6,JS=250;async function XS(t,e=zS,n=JS){for(let r=0;r<e;r++){if(await jn(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var dp=20,fp=6;function YS(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=dp&&Number(e)>=fp}function lp(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Mc(t,e){try{t.send(JSON.stringify(e))}catch{}}function da(t,e){Mc(t,{type:"error",message:e})}function pp(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 $o from"node:fs";import ZS from"node:fs/promises";import xr from"node:path";import QS 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},tv=2e5,Cc=process.env.BOSTRAT_CLAUDE_PROJECTS||xr.join(QS.homedir(),".claude","projects");function Nc(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Pc(t){let e=xr.join(Cc,Nc(t)),n;try{n=$o.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=xr.join(e,o);try{let{mtimeMs:a}=$o.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Me(t,e){if(!e)return null;let n=xr.join(Cc,Nc(t),`${e}.jsonl`);return $o.existsSync(n)?n:null}function hp(t){if(!t)return[];let e=xr.join(Cc,Nc(t)),n;try{n=$o.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=$o.statSync(xr.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Ft(t,e=131072){let n=await ZS.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 ev(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 Oo(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=Me(t,e),o=!0,!r)return null}else if(n){if(r=Pc(t),o=!1,!r)return null}else return null;let s;try{s=ev(await Ft(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(mp,s.model),i=mp[s.model]||tv;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 nv(t,e){return!t||!e?null:Me(t,e)}function rv(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 ov(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 sv(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Dc(t){let e=[],n=t?.message?.content;if(!Array.isArray(n))return e;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)e.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&e.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return e}function wp(t,e){if(!t.uuid)return null;let n=Dc(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 av=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Bc(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Ft(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
52
- `)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let w=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof w=="string"&&av.test(w)&&s.set(m.id,w.split("/").pop())}if(sv(l.content)){let m=wp(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=rv(l.content),d=u==="assistant"?ov(l.content):null,p=u==="user"?wp(c,s):null;!f&&!d&&!p||o.push({role:u,text:f,tools:d||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var iv=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function gp(t,e,{turns:n=16}={}){let r=nv(t,e);if(!r)return"";let o=await Bc(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}:** ${iv(s.text||c,700)} ${i}`.trim()}).join(`
53
-
54
- `):""}import rT from"node:fs";import Ar from"node:fs/promises";import _r from"node:path";import cv from"node:fs/promises";import{execFile as lv}from"node:child_process";import{promisify as uv}from"node:util";var jc=uv(lv),dv=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,yp=new Set,bp=Promise.resolve(),Lc=null;function xp(t){Lc=typeof t=="function"?t:null}function fv(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function Sp({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||yp.has(r)||(yp.add(r),bp=bp.then(()=>pv({sessionName:t,from:e,to:r})).catch(()=>{}))}async function pv({sessionName:t,from:e,to:n}){await G({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await jc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await cv.realpath(r),s=fv(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await jc("npm",["install","-g","@openai/codex"],{env:a,timeout:dv});let i=(await jc(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await G({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await kp({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await G({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await kp({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function kp(t){if(Lc)try{await Lc(t)}catch{}}var mv=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,vp=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function hv(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
55
- `);if(!vp.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(mv);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"&&vp.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 fa=/(^|\n)\s*›/,_p=/^\s*Welcome to Codex\b/;function Sr(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
56
- `);for(let r=n.length-1;r>=0;r--)if(_p.test(n[r]))return n.slice(r).join(`
57
- `);return e}function wv(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 gv(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 Tp=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function yv(t,e=""){let n=Sr(t);if(fa.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(Tp);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(Tp.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&&!_p.test(r[l])&&wv(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=gv(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Ap=/Enter this one-time code/,bv=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function kv(t,e=""){let n=Sr(t);if(fa.test(n)||!Ap.test(n))return null;let r=n.split(`
59
- `),o=r.findIndex(i=>Ap.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(bv);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 xv(t,e=""){let n=Sr(t);return fa.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 Sv(t,e=""){let n=Sr(t);return fa.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 Fc=/Run Everything|Add a follow-up|Plan, search, build anything/;function vv(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||Fc.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 Tv=/click this link to log in|Signing in with the browser/;function Av(t,e=""){let n=String(t);if(!Tv.test(n)||Fc.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 _v(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||Fc.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 Ep(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=kv(e,n)||xv(e,n)||Sv(e,n)||yv(e,n);if(r)return r}if(t==="cursor"){let r=Av(e,n)||_v(e,n)||vv(e,n);if(r)return r}return hv(e,{provider:t,box:n})}function Ip(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 Ev=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Uc=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Mo=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Mt={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key/},cursor:{up:/Run Everything|Add a follow-up|Plan, search, build anything/,busy:/ctrl\+c to stop|^\s*[⠁-⣿]+\s+\S/m,block:/Workspace Trust Required|Trust this workspace|\[a\]\s*Trust|Trusting workspace/,auth:/Press any key to log in|click this link to log in|Scan this QR code to log in|Signing in with the browser/}},Iv=/Trust this workspace|\[a\]\s*Trust/,Rv=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function $v(t){let e=String(t||"");if(!Mt.codex.block.test(e))return null;let n=Sr(e);if(!Mt.codex.block.test(n))return null;let r=n.match(Rv);return{from:r?.[1]||null,to:r?.[2]||null}}function cn(t){return new Promise(e=>setTimeout(e,t))}var Ov=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,pa=new Map;function Rp(t,e){let n=Date.now(),r=Ov();for(let[s,a]of pa)n-a>=r&&pa.delete(s);let o=`${t}::${e}`;return pa.has(o)?!1:(pa.set(o,n),!0)}var Mv=68,Cv=/^\s*[─━]{10,}\s*$/;function Nv(t){let e=0;for(let n of String(t).split(`
62
- `))Cv.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function Hc(t,e){let n=Mt[t];if(!n?.busy)return{state:"idle",reason:null};if(qc(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=Nv(e);return t==="claude"&&r>0&&r<Mv?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Wc(t,e){return Hc(t,e).state}var Pv=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,Dv=15;function Op(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
- `).slice(-Dv).join(`
64
- `);return Pv.test(n)}function Co(t,e){return Wc(t,e)==="busy"}function ma(t,e){return Wc(t,e)!=="idle"}function ha(t,e){let n=Mt[t];return n?.up?n.up.test(e):!0}function Bv(t,e){let n=Mt[t];return n?qc(t,e)?!1:n.up.test(e)&&Wc(t,e)==="idle":!0}function qc(t,e){let n=Mt[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="codex"?!0:!n.up.test(e):!1:!1}async function $p(t,e,n){let r=Date.now()+n,o=0;for(await jn(t).catch(()=>{});;){let s=await mt(t);if(Bv(e,s)){await cn(Mo);return}if(e==="cursor"&&Iv.test(s)&&!Mt.cursor.up.test(s)&&o<3){if(!Rp(t,"cursor-trust")){await cn(Uc);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await Et(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await cn(Mo);continue}if(e==="codex"&&o<3){let a=$v(s);if(a){if(!Rp(t,`codex-update:${a.from}->${a.to}`)){await cn(Uc);continue}o+=1,await G({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"}),Sp({sessionName:t,from:a.from,to:a.to}),await cn(Mo);continue}}if(Date.now()>r){if(qc(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Mt.codex.auth.test(s)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":e==="cursor"&&Mt.cursor.auth.test(s)?a="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":e==="claude"&&Mt.claude.auth.test(s)?a="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":e==="claude"&&Mt.claude.block.test(s)&&(a='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),G({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await kr(t)){let a=`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw G({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),G({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Mt[e]?.block?.test(s),paneUp:!!Mt[e]?.up?.test(s),paneBusy:!!Mt[e]?.busy?.test(s)}).catch(()=>{}),await cn(Mo);return}await cn(Uc)}}function Mp({agent:t,agentCmd:e}={}){let n=e?on(e):on(t||"");return{label:n,realClaude:e?Dn(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 wa({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Mp({agent:n,agentCmd:r});return!e||!o&&!s?null:ln({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function ln({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=Ev()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Mp({agent:r,agentCmd:o});if(i&&t&&await tn(t),c&&t&&await dr(t),l&&t&&await fr(t),(i||c||l)&&n){await $p(n,a,s);return}let u=Mt[r]?r:Mt[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await $p(n,u,s);return}await cn(Mo)}var jv=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),Lv=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function Ce(t){return t==="codex"?{enter:!0,submitDelayMs:jv}:t==="claude"?{enter:!0,submitDelayMs:Lv}:{enter:!0}}import ya from"node:fs/promises";import jp from"node:path";var Fv=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],Uv={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 vr(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of Fv){let o=0;for(let s of Uv[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var Hv=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),Wv=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Cp(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))Hv.has(r)&&(e+=o||0),Wv.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function No(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 Np=1.5,Pp=.75,qv=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 Bp(t){let e=No(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||qv.has(r)||n.push(r);return n}function Kv(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(Dp(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(Dp(n,"assistant",o))}}return e}function Dp(t,e,n){let r=vr(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 Po(t){let e=Kv(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=Bp(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 ga(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(Bp(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,w]of f){let y=t.lengths[m],b=w+Np*(1-Pp+Pp*(y/(t.avgdl||1))),S=p*(w*(Np+1)/b);c.set(m,(c.get(m)||0)+S)}}let l=[];for(let[u,f]of c){let d=t.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var Lp="Relevant prior discussion (verbatim, retrieved";async function Gv(t){let e=jp.join(t,".bostrat","events"),n;try{n=await ya.readdir(e)}catch{return Po([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await ya.readFile(jp.join(e,o),"utf8");for(let a of s.split(`
65
- `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Po(r)}function Vv(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 zv(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=ga(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?["",`## ${Lp}, 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 Fp(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await ya.readFile(e,"utf8");if(a.includes(Lp))return{appended:!1,reason:"already-enriched"};let i=await Gv(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=zv(i,Vv(a),{k:r,maxChars:o,exclude:s});return c?(await ya.appendFile(e,`
45
+ `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${br}: ${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&&ia())try{e.paneTail=Mo((await mt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(ia()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await Tv(e)}async function np({limit:t=80}={}){try{return(await Oo.readFile(br,"utf8")).split(`
46
+ `).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Mo(o.paneTail)),o}catch{return{raw:Mo(r)}}})}catch{return[]}}var rp={DEBUG_DIR:Rc,LOG_FILE:br};var rt=_v(Av);function vr(t){return new Promise(e=>setTimeout(e,t))}function ca(t,e){if(!e)return t;let n=o=>typeof o=="string"?o.split(e).join("<redacted>"):o,r=new Error(n(t?.message)||"command failed");return t?.code!==void 0&&(r.code=t.code),t?.stderr!==void 0&&(r.stderr=n(t.stderr)),r}var M=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";function sp(t){return`/tmp/tmux-${t}/default`}function Iv({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=sp(r);return e?o:n?`${n}/bostrat/tmux-default`:o}var op=typeof process.getuid=="function"?process.getuid():0,Vt=Iv({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return la.existsSync(sp(op))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:op}),Rv=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Oc=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",$v="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";function Mc(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${$v}=1`]}var Cc="bostrat-usage-probe",Ov=new Set([Cc]);function Nc(t,e){let n=Ln(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function Ln(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function sn(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function Mv(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:Ln(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function ap(t,{loginShell:e=!1,failOpen:n=!1}={}){let r=await ip(t,{loginShell:e});return r===ua?!0:r===kr?!1:n}var ua="present",kr="absent",xr="unknown",$c=5e3,Cv=[250,1e3,3e3],Nv=t=>new Promise(e=>setTimeout(e,t));async function ip(t,{loginShell:e=!1,timeoutMs:n=$c}={}){if(!t)return kr;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await rt(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:n}),ua}catch(s){return s?.killed||s?.signal?xr:s?.code===1||s?.code===127?kr:xr}}async function da(t,{retries:e=Cv,sleep:n=Nv,probe:r=ip}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:$c});for(let a=0;s===xr&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:$c*2});return s}var Pc={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 N(...t){return["-S",Vt,...t]}async function an(){if(!la.existsSync(Vt))return!1;try{return await rt(M,N("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function Co(){if(await an())return!0;try{la.mkdirSync(Vt.replace(/\/[^/]+$/,""),{recursive:!0}),await rt(M,N("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await an()}var Pv=/^[A-Za-z0-9_.-]{1,64}$/;function yt(t){return typeof t=="string"&&Pv.test(t)}async function $e(){if(!la.existsSync(Vt))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 rt(M,N("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=>!Ov.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function tt(t){return(await $e()).find(n=>n.name===t)||null}var Dv=/^[A-Za-z0-9 ._↻-]+$/,Bv=64;function jv(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=Bv&&Dv.test(e)}function Dc(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function cp(t){try{let{stdout:e}=await rt(M,N("list-sessions","-F","#{session_name}"));return e.split(`
48
+ `).map(n=>n.trim()).includes(t)}catch{return!1}}var Lv=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,Fv=1e3,Uv=/not found|Unknown command/i;function Hv(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 Wv(t,e){let n=Date.now()+Lv(),r=Hv(e),o=!1,s=0;for(;;){if(await vr(Fv),s+=1,!await cp(t))return;let a=await mt(t,{lines:15}),i=!o&&Uv.test(a)&&!!r?.test(a);if(!i&&s>1&&!await Sr(t))return;if(i){o=!0,await q({flow:"spawn",phase:"launch-retry",sessionName:t,agent:sn(e),promptHead:e.slice(0,120)}).catch(()=>{}),await rt(M,N("send-keys","-t",t,"C-u")),await rt(M,N("send-keys","-t",t,"-l",e)),await rt(M,N("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await q({flow:"spawn",phase:"launch-failed",sessionName:t,agent:sn(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Fn(t,e=Rv,n={}){if(!jv(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Dc(r);if(!yt(o))throw new Error("resulting tmux session name is invalid");if(!await Co())throw new Error(`host tmux server not running on ${Vt} and auto-start failed \u2014 start one: ${M} -S ${Vt} new-session -d -s keeper "sleep 2147483647"`);if(await cp(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Oc,a=n.agent||sn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||Ev(),l=i?s:Nc(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&Ln(s),d=n.firstRun===!0;d||(f&&await en(e),a==="codex"&&await mr(e),a==="cursor"&&await hr(e),await jf(e),await Qf(e));let p=f&&!d?Xs():null,m=pf(),w=["-e",`${Vs}=${m}`];p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&w.push(...Mc());try{await rt(M,N("new-session","-d","-s",o,"-c",e,...w))}catch(y){throw ca(p?ca(y,p):y,m)}return await Un(o),await rt(M,N("set-option","-t",o,"@bostrat-session","1")),await rt(M,N("set-option","-t",o,"@bostrat-title",r)),await rt(M,N("set-option","-t",o,"@bostrat-sid",c)),await rt(M,N("set-option","-t",o,"@bostrat-agent",a)),await rt(M,N("send-keys","-t",o,l,"Enter")),Mv(l)&&Wv(o,l).catch(()=>{}),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function pe(t){if(!yt(t))throw new Error("invalid session name");await rt(M,N("kill-session","-t",t))}async function lp(t,e){if(yt(t))try{let n=e?N("set-option","-t",t,"@bostrat-sid",e):N("set-option","-u","-t",t,"@bostrat-sid");await rt(M,n)}catch{}}async function mt(t,{lines:e=40}={}){if(!yt(t))return"";try{let n=N("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await rt(M,n);return String(r||"")}catch{return""}}var qv=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,Kv=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,At=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,Gv=Number(process.env.BOSTRAT_BOX_COLS)||120,Vv=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function up(t){if(!yt(t))return 0;try{let{stdout:e}=await rt(M,N("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
49
+ `).filter(Boolean).length}catch{return 0}}async function Un(t,{cols:e=Gv,rows:n=Vv}={}){if(!yt(t))return!0;if(await up(t))return!1;try{let r=await dp(t);if(r.cols===e&&r.rows===n)return!0;await rt(M,N("resize-window","-t",t,"-x",String(e),"-y",String(n))),await rt(M,N("set-window-option","-t",t,"window-size","latest")),await vr(80)}catch{}return!0}async function zv(t){if(!yt(t))return 0;try{let{stdout:e}=await rt(M,N("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function Sr(t){let e=await zv(t);if(!e)return!1;try{return await rt("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function dp(t){if(!yt(t))return{cols:0,rows:0};try{let{stdout:e}=await rt(M,N("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 fa(t,{cols:e=qv,rows:n=Kv}={}){if(yt(t)&&!await up(t))try{let r=await dp(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await rt(M,N("resize-window","-t",t,"-x",String(o),"-y",String(s))),await rt(M,N("set-window-option","-t",t,"window-size","latest")),await vr(80))}catch{}}async function cn(t){return await fa(t),mt(t,{lines:At})}var Jv=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function St(t,e,{settleMs:n=40,kind:r}={}){if(!yt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>Jv.has(s));for(let s=0;s<o.length;s++)await rt(M,N("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await vr(n);return q({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function pa(t,e,{kind:n}={}){if(!yt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await rt(M,N("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 ma(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!yt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await rt(M,N("send-keys","-t",t,"-l",s)),n>0&&await vr(n);q({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function _t(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!yt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await rt(M,N("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await vr(s),await rt(M,N("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 Xv from"node-pty";async function pp(t,e,n={}){if(!yt(e)){ha(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await an()){ha(t,`host tmux server not running on ${Vt}`),t.close(1011,"tmux server unavailable");return}if(!await tt(e)){ha(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=fp(n.cols,120,mp),o=fp(n.rows,32,hp),s;try{s=Xv.spawn(M,["-S",Vt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ha(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Bc(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Bc(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"&&tS(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{}Qv(e)})}var Yv=6,Zv=250;async function Qv(t,e=Yv,n=Zv){for(let r=0;r<e;r++){if(await Un(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var mp=20,hp=6;function tS(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=mp&&Number(e)>=hp}function fp(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Bc(t,e){try{t.send(JSON.stringify(e))}catch{}}function ha(t,e){Bc(t,{type:"error",message:e})}function wp(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 No from"node:fs";import eS from"node:fs/promises";import Tr from"node:path";import nS from"node:os";var gp={"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},rS=2e5,jc=process.env.BOSTRAT_CLAUDE_PROJECTS||Tr.join(nS.homedir(),".claude","projects");function Lc(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Fc(t){let e=Tr.join(jc,Lc(t)),n;try{n=No.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Tr.join(e,o);try{let{mtimeMs:a}=No.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Oe(t,e){if(!e)return null;let n=Tr.join(jc,Lc(t),`${e}.jsonl`);return No.existsSync(n)?n:null}function yp(t){if(!t)return[];let e=Tr.join(jc,Lc(t)),n;try{n=No.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=No.statSync(Tr.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Lt(t,e=131072){let n=await eS.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 oS(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 Po(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=Oe(t,e),o=!0,!r)return null}else if(n){if(r=Fc(t),o=!1,!r)return null}else return null;let s;try{s=oS(await Lt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(gp,s.model),i=gp[s.model]||rS;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 sS(t,e){return!t||!e?null:Oe(t,e)}function aS(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 iS(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 cS(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Uc(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 bp(t,e){if(!t.uuid)return null;let n=Uc(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 lS=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Hc(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Lt(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
52
+ `)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let w=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof w=="string"&&lS.test(w)&&s.set(m.id,w.split("/").pop())}if(cS(l.content)){let m=bp(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=aS(l.content),d=u==="assistant"?iS(l.content):null,p=u==="user"?bp(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 kp(t,e,{turns:n=16}={}){let r=sS(t,e);if(!r)return"";let o=await Hc(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 aT from"node:fs";import Ir from"node:fs/promises";import Rr from"node:path";import dS from"node:fs/promises";import{execFile as fS}from"node:child_process";import{promisify as pS}from"node:util";var Wc=pS(fS),mS=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,xp=new Set,vp=Promise.resolve(),qc=null;function Tp(t){qc=typeof t=="function"?t:null}function hS(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function Ap({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||xp.has(r)||(xp.add(r),vp=vp.then(()=>wS({sessionName:t,from:e,to:r})).catch(()=>{}))}async function wS({sessionName:t,from:e,to:n}){await q({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Wc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await dS.realpath(r),s=hS(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Wc("npm",["install","-g","@openai/codex"],{env:a,timeout:mS});let i=(await Wc(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 Sp({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 Sp({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function Sp(t){if(qc)try{await qc(t)}catch{}}var gS=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,_p=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function yS(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
55
+ `);if(!_p.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(gS);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"&&_p.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 wa=/(^|\n)\s*›/,Rp=/^\s*Welcome to Codex\b/;function Ar(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
56
+ `);for(let r=n.length-1;r>=0;r--)if(Rp.test(n[r]))return n.slice(r).join(`
57
+ `);return e}function bS(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 kS(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 Ep=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function xS(t,e=""){let n=Ar(t);if(wa.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(Ep);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(Ep.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&&!Rp.test(r[l])&&bS(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=kS(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Ip=/Enter this one-time code/,vS=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function SS(t,e=""){let n=Ar(t);if(wa.test(n)||!Ip.test(n))return null;let r=n.split(`
59
+ `),o=r.findIndex(i=>Ip.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(vS);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 TS(t,e=""){let n=Ar(t);return wa.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 AS(t,e=""){let n=Ar(t);return wa.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 Kc=/Run Everything|Add a follow-up|Plan, search, build anything/;function _S(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||Kc.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 ES=/click this link to log in|Signing in with the browser/;function IS(t,e=""){let n=String(t);if(!ES.test(n)||Kc.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 RS(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||Kc.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 $p(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=SS(e,n)||TS(e,n)||AS(e,n)||xS(e,n);if(r)return r}if(t==="cursor"){let r=IS(e,n)||RS(e,n)||_S(e,n);if(r)return r}return yS(e,{provider:t,box:n})}function Op(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 $S=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Gc=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Do=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,$t={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,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/}},OS=/Trust this workspace|\[a\]\s*Trust/,MS=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function CS(t){let e=String(t||"");if(!$t.codex.block.test(e))return null;let n=Ar(e);if(!$t.codex.block.test(n))return null;let r=n.match(MS);return{from:r?.[1]||null,to:r?.[2]||null}}function ln(t){return new Promise(e=>setTimeout(e,t))}var NS=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,ga=new Map;function Mp(t,e){let n=Date.now(),r=NS();for(let[s,a]of ga)n-a>=r&&ga.delete(s);let o=`${t}::${e}`;return ga.has(o)?!1:(ga.set(o,n),!0)}var PS=68,DS=/^\s*[─━]{10,}\s*$/;function BS(t){let e=0;for(let n of String(t).split(`
62
+ `))DS.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function Vc(t,e){let n=$t[t];if(!n?.busy)return{state:"idle",reason:null};if(Jc(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=BS(e);return t==="claude"&&r>0&&r<PS?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function zc(t,e){return Vc(t,e).state}var jS=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,LS=15;function Np(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
+ `).slice(-LS).join(`
64
+ `);return jS.test(n)}function Bo(t,e){return zc(t,e)==="busy"}function ya(t,e){return zc(t,e)!=="idle"}function ba(t,e){let n=$t[t];return n?.up?n.up.test(e):!0}function FS(t,e){let n=$t[t];return n?Jc(t,e)?!1:n.up.test(e)&&zc(t,e)==="idle":!0}function Jc(t,e){let n=$t[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 Cp(t,e,n){let r=Date.now()+n,o=0;for(await Un(t).catch(()=>{});;){let s=await mt(t);if(FS(e,s)){await ln(Do);return}if(e==="cursor"&&OS.test(s)&&!$t.cursor.up.test(s)&&o<3){if(!Mp(t,"cursor-trust")){await ln(Gc);continue}o+=1,await q({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await _t(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await ln(Do);continue}if(e==="codex"&&o<3){let a=CS(s);if(a){if(!Mp(t,`codex-update:${a.from}->${a.to}`)){await ln(Gc);continue}o+=1,await q({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await _t(t,"3",{enter:!1,kind:"codex-update-skip"}),Ap({sessionName:t,from:a.from,to:a.to}),await ln(Do);continue}}if(Date.now()>r){if(Jc(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=$t.codex.auth.test(s)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":e==="cursor"&&$t.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"&&$t.claude.auth.test(s)?a="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":e==="claude"&&$t.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 Sr(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:!!$t[e]?.block?.test(s),paneUp:!!$t[e]?.up?.test(s),paneBusy:!!$t[e]?.busy?.test(s)}).catch(()=>{}),await ln(Do);return}await ln(Gc)}}function Pp({agent:t,agentCmd:e}={}){let n=e?sn(e):sn(t||"");return{label:n,realClaude:e?Ln(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 ka({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Pp({agent:n,agentCmd:r});return!e||!o&&!s?null:un({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function un({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=$S()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Pp({agent:r,agentCmd:o});if(i&&t&&await en(t),c&&t&&await mr(t),l&&t&&await hr(t),(i||c||l)&&n){await Cp(n,a,s);return}let u=$t[r]?r:$t[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Cp(n,u,s);return}await ln(Do)}var US=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),HS=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function Me(t){return t==="codex"?{enter:!0,submitDelayMs:US}:t==="claude"?{enter:!0,submitDelayMs:HS}:{enter:!0}}import va from"node:fs/promises";import Up from"node:path";var WS=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],qS={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 _r(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of WS){let o=0;for(let s of qS[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var KS=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),GS=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Dp(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))KS.has(r)&&(e+=o||0),GS.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function jo(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 Bp=1.5,jp=.75,VS=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 Fp(t){let e=jo(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||VS.has(r)||n.push(r);return n}function zS(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(Lp(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(Lp(n,"assistant",o))}}return e}function Lp(t,e,n){let r=_r(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 Lo(t){let e=zS(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=Fp(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 xa(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(Fp(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,w]of f){let y=t.lengths[m],k=w+Bp*(1-jp+jp*(y/(t.avgdl||1))),v=p*(w*(Bp+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 Hp="Relevant prior discussion (verbatim, retrieved";async function JS(t){let e=Up.join(t,".bostrat","events"),n;try{n=await va.readdir(e)}catch{return Lo([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await va.readFile(Up.join(e,o),"utf8");for(let a of s.split(`
65
+ `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Lo(r)}function XS(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 YS(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=xa(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?["",`## ${Hp}, 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 Wp(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await va.readFile(e,"utf8");if(a.includes(Hp))return{appended:!1,reason:"already-enriched"};let i=await JS(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=YS(i,XS(a),{k:r,maxChars:o,exclude:s});return c?(await va.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 Kc from"node:fs/promises";import Tr from"node:path";import Jv from"node:os";import{EventEmitter as Xv}from"node:events";import{randomUUID as Yv}from"node:crypto";var Zv=new Set(["session.ready","turn.user","turn.agent","turn.retracted","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"]),Qv=new Set(["user","agent","orchestrator","system"]),tT=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Ut=new Xv;Ut.setMaxListeners(0);var Gc=process.env.BOSTRAT_STATE_DIR||Tr.join(process.env.HOME||Jv.tmpdir(),".bostrat");function ka(t){return t?.cwd?Tr.join(t.cwd,".bostrat","events"):Tr.join(Gc,"events")}var Up=(t,e)=>Tr.join(t,`${e}.ndjson`),ba=new Map;async function eT(t,e){if(!ba.has(e)){let o=(await L(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);ba.set(e,o)}let n=ba.get(e)+1;return ba.set(e,n),n}function nT(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function I(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!Zv.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!Qv.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!tT.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Tr.join(Gc,"events"):ka(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=nT(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await eT(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||Yv(),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 Kc.mkdir(r,{recursive:!0}),await Kc.appendFile(Up(r,n),JSON.stringify(i)+`
70
- `),Ut.emit("event",i),Ut.emit(`track:${n}`,i),i}async function L(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Tr.join(Gc,"events"):ka(t));let o;try{o=await Kc.readFile(Up(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 Hp(t,e){let n=`track:${t}`;return Ut.on(n,e),()=>Ut.off(n,e)}var Gp=".bostrat/handoff",Vp="latest.md",Wp=32*1024;function oT(t){let e=Io(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=Wp?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,Wp).toString("utf8").replace(/�+$/,"")}
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 Xc from"node:fs/promises";import Er from"node:path";import ZS from"node:os";import{EventEmitter as QS}from"node:events";import{randomUUID as tT}from"node:crypto";var eT=new Set(["session.ready","turn.user","turn.agent","turn.retracted","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"]),rT=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Ft=new QS;Ft.setMaxListeners(0);var Yc=process.env.BOSTRAT_STATE_DIR||Er.join(process.env.HOME||ZS.tmpdir(),".bostrat");function Ta(t){return t?.cwd?Er.join(t.cwd,".bostrat","events"):Er.join(Yc,"events")}var qp=(t,e)=>Er.join(t,`${e}.ndjson`),Sa=new Map;async function oT(t,e){if(!Sa.has(e)){let o=(await L(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);Sa.set(e,o)}let n=Sa.get(e)+1;return Sa.set(e,n),n}function sT(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function R(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!eT.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&&!rT.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Er.join(Yc,"events"):Ta(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=sT(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await oT(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||tT(),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 Xc.mkdir(r,{recursive:!0}),await Xc.appendFile(qp(r,n),JSON.stringify(i)+`
70
+ `),Ft.emit("event",i),Ft.emit(`track:${n}`,i),i}async function L(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Er.join(Yc,"events"):Ta(t));let o;try{o=await Xc.readFile(qp(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 Kp(t,e){let n=`track:${t}`;return Ft.on(n,e),()=>Ft.off(n,e)}var Jp=".bostrat/handoff",Xp="latest.md",Gp=32*1024;function iT(t){let e=Mo(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=Gp?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,Gp).toString("utf8").replace(/�+$/,"")}
72
72
 
73
- [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var zp="Checkpoint: write a work-in-progress handoff",Jp="HANDOFF WRITTEN",Xp="Resume from .bostrat/handoff/latest.md",sT="/clear",aT=t=>`${zp} 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 ${Jp} and take no further action.`,iT=`${Xp}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Yp="Beat: write a",Zp="BEAT WRITTEN",cT=".bostrat/arc",lT="beat.txt",qp=200,Qp=4,tm=5,uT=36,dT=t=>`${Yp} 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 ${Qp} further lines, each starting with "- " and naming ONE piece in at most ${tm} 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 ${Zp} and take no further action.`,Kp=/^(?:[-–—*•·]|\d+[.)])\s+/,fT=(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 pT(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Kp.test(s)){if(r.length>=Qp)continue;let a=fT(s.replace(Kp,"").replace(/[.;,:]+$/,""),tm,uT);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>qp&&(o=`${o.slice(0,qp-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function em(t,e,{timeoutMs:n=9e4}={}){let r=_r.join(e,cT),o=_r.join(r,lT);await Ar.mkdir(r,{recursive:!0});let s=await xa(o),a=await rt(t);if(!a)throw new Error(`session "${t}" not found`);return await Sa(t,a,dT(o)),await lm(o,s,n),pT(await Ar.readFile(o,"utf8"))}function nm(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Jp||n===Zp:n.startsWith(zp)||n===sT||n.startsWith(Xp)||n.startsWith(Yp):!1}var mT=6e5;function rm(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-mT;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 om(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 sm(t,{windows:e=[]}={}){if(nm(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 hT=3500,wT=3e5,Vc=new Map;function va(t){return Vc.get(t)||null}async function am(t){let e=Vc.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await rt(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:_r.join(n.cwd,Gp,Vp)};return Vc.set(t,r),gT(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function gT(t,e){t.step="summarize",await im(t.name,e),t.step="clear";let n=await rt(t.name);await Sa(t.name,n,"/clear"),await um(hT),t.step="resume",await Sa(t.name,n,iT),t.step="done",t.finishedAt=Date.now()}async function im(t,e){let n=_r.join(e,Gp),r=_r.join(n,Vp);await Ar.mkdir(n,{recursive:!0});let o=await xa(r),s=await rt(t);await G({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await Sa(t,s,aT(r)),await lm(r,o,wT),await G({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await xa(r)});let a=await Fp(e,r,{exclude:l=>nm(l.role,l.text)});await G({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:r,...a});let i=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(i){await I({id:i,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...a,tokensEst:a.chars?Math.round(a.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let l=await Ar.readFile(r,"utf8");await I({id:i,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...oT(l),ts:Date.now()}})}catch{}}await jf(e);let c=await yT(n,r);return i&&await Jf(i,c||r,{box:t,agent:s?.agent??null}),r}async function cm(t){let e=await rt(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return im(t,e.cwd)}async function xa(t){try{return(await Ar.stat(t)).mtimeMs}catch{return 0}}async function lm(t,e,n){let r=Date.now()+n;for(;;){if(await xa(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await um(2e3)}}async function yT(t,e){try{if(!rT.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=_r.join(t,`${n}.md`);return await Ar.copyFile(e,r),r}catch{return null}}function um(t){return new Promise(e=>setTimeout(e,t))}async function Sa(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 ln({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await rt(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await Et(t,n,{kind:"orchestration-drive",...Ce(r.agent)})}import F from"node:fs/promises";import U from"node:path";import{randomUUID as Ts}from"node:crypto";import{execFile as WR}from"node:child_process";import{promisify as qR}from"node:util";import Jh from"node:path";var zc={"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)."}]},dm={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"},bT=/^\d{4}-\d{2}-\d{2}$/;function fm(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!bT.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 kT(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:fm(o.from,"from",n),toMs:fm(o.to,"to",n)}))}return e}var xT=kT(zc);function ST(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 vT(t){if(!t)return null;let e=String(t).trim();return zc[e]?e:dm[e]?dm[e]:Object.keys(zc).find(r=>e.startsWith(r))||null}function pm(t,e){let n=vT(t);if(!n)return null;let r=ST(e);return r===null?null:xT[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}var Ta=(t,e)=>pm(t,e)!==null;function me(t,e={},n){let r=pm(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 mm=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function TT(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function hm(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function Ln(t){let e=t.payload?.model||"unknown",n=TT(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=!1;!r&&n&&(r=me(e,n),r>0&&(o=!0));let s=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,a=!r&&s>0&&!Ta(e);return{usd:r,model:e,tokens:n,repriced:o,unpriced:a}}function he(t,{boxLabels:e={}}={}){let n=(t||[]).filter(f=>f.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=new Set,u=mm();for(let f of n){let{usd:d,model:p,tokens:m,repriced:w,unpriced:y}=Ln(f),b=f.provider||"unknown",S=f.boxId||"unattributed";if(w&&i++,y&&(c++,l.add(p)),a+=d,hm(u,m),r[p]||={usd:0,tokens:mm(),events:0},r[p].usd+=d,r[p].events+=1,hm(r[p].tokens,m),o[b]||={usd:0,events:0},o[b].usd+=d,o[b].events+=1,s[S]||={usd:0,events:0,provider:b,models:new Set,label:e[S]||S},s[S].usd+=d,s[S].events+=1,p!=="unknown"&&s[S].models.add(p),f.epoch!=null){let E=(s[S].epochs||={})[f.epoch]||={usd:0,events:0,provider:b,models:new Set};E.usd+=d,E.events+=1,E.provider=b,p!=="unknown"&&E.models.add(p)}}return{totalUsd:Do(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:Aa(r,f=>({...f,usd:Do(f.usd)})),byProvider:Aa(o,f=>({...f,usd:Do(f.usd)})),byBox:Aa(s,f=>({usd:Do(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:Aa(f.epochs,d=>({usd:Do(d.usd),events:d.events,provider:d.provider,models:[...d.models]}))}:{}}))}}var Do=t=>Math.round(t*1e4)/1e4,Aa=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import At from"node:fs/promises";import Ht from"node:path";import gm from"node:os";import{randomUUID as IT}from"node:crypto";import{execFile as RT}from"node:child_process";import{promisify as $T}from"node:util";import{chromium as OT}from"playwright-core";import Jc from"node:os";import{execFile as AT}from"node:child_process";import{promisify as _T}from"node:util";var ET=_T(AT);async function _a(){if(process.platform!=="darwin")return Jc.freemem();try{let{stdout:t}=await ET("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:Jc.freemem()}catch{return Jc.freemem()}}var MT=$T(RT),ym=process.env.BOSTRAT_STATE_DIR||Ht.join(process.env.HOME||gm.tmpdir(),".bostrat"),bm=Ht.join(ym,"mocks.json"),CT=process.env.BOSTRAT_MOCK_ENGINE||"chromium",NT=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",PT=process.env.BOSTRAT_FIREFOX_BIN||"firefox",DT=1300,Er=2,BT=32,km=1500,Ir=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,jT=700*1024*1024,oe=class extends Error{constructor(e,n=""){super(n?`${e}
73
+ [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var Yp="Checkpoint: write a work-in-progress handoff",Zp="HANDOFF WRITTEN",Qp="Resume from .bostrat/handoff/latest.md",cT="/clear",lT=t=>`${Yp} 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 ${Zp} and take no further action.`,uT=`${Qp}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,tm="Beat: write a",em="BEAT WRITTEN",dT=".bostrat/arc",fT="beat.txt",Vp=200,nm=4,rm=5,pT=36,mT=t=>`${tm} 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 ${nm} further lines, each starting with "- " and naming ONE piece in at most ${rm} 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 ${em} and take no further action.`,zp=/^(?:[-–—*•·]|\d+[.)])\s+/,hT=(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 wT(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(zp.test(s)){if(r.length>=nm)continue;let a=hT(s.replace(zp,"").replace(/[.;,:]+$/,""),rm,pT);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>Vp&&(o=`${o.slice(0,Vp-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function om(t,e,{timeoutMs:n=9e4}={}){let r=Rr.join(e,dT),o=Rr.join(r,fT);await Ir.mkdir(r,{recursive:!0});let s=await Aa(o),a=await tt(t);if(!a)throw new Error(`session "${t}" not found`);return await _a(t,a,mT(o)),await fm(o,s,n),wT(await Ir.readFile(o,"utf8"))}function sm(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Zp||n===em:n.startsWith(Yp)||n===cT||n.startsWith(Qp)||n.startsWith(tm):!1}var gT=6e5;function am(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-gT;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 im(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 cm(t,{windows:e=[]}={}){if(sm(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 yT=3500,bT=3e5,Zc=new Map;function Ea(t){return Zc.get(t)||null}async function lm(t){let e=Zc.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:Rr.join(n.cwd,Jp,Xp)};return Zc.set(t,r),kT(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function kT(t,e){t.step="summarize",await um(t.name,e),t.step="clear";let n=await tt(t.name);await _a(t.name,n,"/clear"),await pm(yT),t.step="resume",await _a(t.name,n,uT),t.step="done",t.finishedAt=Date.now()}async function um(t,e){let n=Rr.join(e,Jp),r=Rr.join(n,Xp);await Ir.mkdir(n,{recursive:!0});let o=await Aa(r),s=await tt(t);await q({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await _a(t,s,lT(r)),await fm(r,o,bT),await q({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await Aa(r)});let a=await Wp(e,r,{exclude:l=>sm(l.role,l.text)});await q({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:r,...a});let i=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(i){await R({id:i,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...a,tokensEst:a.chars?Math.round(a.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let l=await Ir.readFile(r,"utf8");await R({id:i,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...iT(l),ts:Date.now()}})}catch{}}await Uf(e);let c=await xT(n,r);return i&&await Zf(i,c||r,{box:t,agent:s?.agent??null}),r}async function dm(t){let e=await tt(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return um(t,e.cwd)}async function Aa(t){try{return(await Ir.stat(t)).mtimeMs}catch{return 0}}async function fm(t,e,n){let r=Date.now()+n;for(;;){if(await Aa(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await pm(2e3)}}async function xT(t,e){try{if(!aT.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Rr.join(t,`${n}.md`);return await Ir.copyFile(e,r),r}catch{return null}}function pm(t){return new Promise(e=>setTimeout(e,t))}async function _a(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 un({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 _t(t,n,{kind:"orchestration-drive",...Me(r.agent)})}import F from"node:fs/promises";import U from"node:path";import{randomUUID as Es}from"node:crypto";import{execFile as GR}from"node:child_process";import{promisify as VR}from"node:util";import Qh from"node:path";var Qc={"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)."}]},mm={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"},vT=/^\d{4}-\d{2}-\d{2}$/;function hm(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!vT.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 ST(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:hm(o.from,"from",n),toMs:hm(o.to,"to",n)}))}return e}var TT=ST(Qc);function AT(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 _T(t){if(!t)return null;let e=String(t).trim();return Qc[e]?e:mm[e]?mm[e]:Object.keys(Qc).find(r=>e.startsWith(r))||null}function wm(t,e){let n=_T(t);if(!n)return null;let r=AT(e);return r===null?null:TT[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}var gm=(t,e)=>wm(t,e)!==null;function dn(t,e={},n){let r=wm(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 ym=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function ET(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function bm(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function Ce(t){let e=t.payload?.model||"unknown",n=ET(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=t.source==="vendor",s=r,a=!1;if(n&&!o){let l=dn(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&&!gm(e,t.ts);return{usd:s,model:e,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function me(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=ym();for(let d of n){let{usd:p,model:m,tokens:w,repriced:y,unpriced:k,asRecorded:v}=Ce(d);i+=v;let E=d.provider||"unknown",$=d.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(m)),a+=p,bm(f,w),r[m]||={usd:0,tokens:ym(),events:0},r[m].usd+=p,r[m].events+=1,bm(r[m].tokens,w),o[E]||={usd:0,events:0},o[E].usd+=p,o[E].events+=1,s[$]||={usd:0,events:0,provider:E,models:new Set,label:e[$]||$},s[$].usd+=p,s[$].events+=1,m!=="unknown"&&s[$].models.add(m),d.epoch!=null){let z=(s[$].epochs||={})[d.epoch]||={usd:0,events:0,provider:E,models:new Set};z.usd+=p,z.events+=1,z.provider=E,m!=="unknown"&&z.models.add(m)}}return{totalUsd:$r(a),asRecordedUsd:$r(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Ia(r,d=>({...d,usd:$r(d.usd)})),byProvider:Ia(o,d=>({...d,usd:$r(d.usd)})),byBox:Ia(s,d=>({usd:$r(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Ia(d.epochs,p=>({usd:$r(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var $r=t=>Math.round(t*1e4)/1e4,Ia=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import Tt from"node:fs/promises";import Ut from"node:path";import xm from"node:os";import{randomUUID as OT}from"node:crypto";import{execFile as MT}from"node:child_process";import{promisify as CT}from"node:util";import{chromium as NT}from"playwright-core";import tl from"node:os";import{execFile as IT}from"node:child_process";import{promisify as RT}from"node:util";var $T=RT(IT);async function Ra(){if(process.platform!=="darwin")return tl.freemem();try{let{stdout:t}=await $T("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:tl.freemem()}catch{return tl.freemem()}}var PT=CT(MT),vm=process.env.BOSTRAT_STATE_DIR||Ut.join(process.env.HOME||xm.tmpdir(),".bostrat"),Sm=Ut.join(vm,"mocks.json"),DT=process.env.BOSTRAT_MOCK_ENGINE||"chromium",BT=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",jT=process.env.BOSTRAT_FIREFOX_BIN||"firefox",LT=1300,Or=2,FT=32,Tm=1500,Mr=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,UT=700*1024*1024,re=class extends Error{constructor(e,n=""){super(n?`${e}
74
74
  ${n.trim().split(`
75
75
  `).slice(-6).join(`
76
- `)}`:e),this.name="MockRenderError"}},Fn=null,Xc=null;async function ge(){return Fn||(Xc||=(async()=>{try{Fn=JSON.parse(await At.readFile(bm,"utf8"))}catch{Fn={mocks:[]}}finally{Xc=null}return Fn.mocks||=[],Fn})(),Xc)}async function we(){await Lt(bm,Fn)}var un=t=>Dt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Un=(t,e,n)=>rn("mocks",t.id,e,n,Jt(t,e)[n]).catch(()=>{});function se(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var Ea=t=>Ht.join(t,".bostrat","mocks");function Jt(t,e){let n=Ea(t.cwd);return{html:Ht.join(n,`${t.id}.v${e}.html`),png:Ht.join(n,`${t.id}.v${e}.png`)}}function Ne(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 Zc(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var LT=Buffer.from([137,80,78,71,13,10,26,10]);function xm(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(LT)}async function FT(t){let e=Buffer.alloc(24),n=await At.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function UT({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 HT(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+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 WT(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 qT({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await OT.launch({executablePath:NT,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new oe("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Er})).newPage();a.setDefaultTimeout(Ir),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Ir}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ir});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await HT(a,i),u=l&&UT({...l,pad:BT*Er,align:Er});u&&(i=await WT(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<km)throw new oe(`capture produced a ${i.length}-byte image`);let c=Zc(i);if(!c)throw new oe("output is not a PNG");return await At.writeFile(e,i),{width:Math.round(c.width/Er),height:Math.round(c.height/Er),dpr:Er,renderMs:Date.now()-r}}catch(s){throw s instanceof oe?s:new oe(/timeout/i.test(s?.message||"")?`render timed out after ${Ir}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function wm({htmlPath:t,outPath:e,width:n}){let r=await At.mkdtemp(Ht.join(gm.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await MT(PT,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ir,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new oe(c?.killed?`render timed out after ${Ir}ms`:"firefox exited non-zero",s)}let a=await At.stat(e).catch(()=>null);if(!a||a.size<km)throw new oe(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Zc(await FT(e));if(!i)throw new oe("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await At.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function KT(t){if(CT==="firefox")return wm(t);try{return await qT(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return wm(t);throw e}}var GT=KT;var Yc=Promise.resolve();function Sm(t){return Yc=Yc.then(t,t),Yc}async function vm(t,e){let n=await ge(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await we(),await se(r,"rendering",{version:e});try{let o=await _a();if(o<jT)throw new oe(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Jt(r,e),a=await GT({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await At.rm(s.png,{force:!0}).catch(()=>{});return}Tm(r,{v:e,source:"html",htmlRel:Ht.relative(r.cwd,s.html),pngRel:Ht.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await we(),await se(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),un(r),Un(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await we(),await se(r,"error",{version:e,error:r.error}),un(r)}}}function Tm(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 VT({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?DT:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Hn({trackId:t,group:e}={}){let n=await ge(),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 dn(t){let n=(await ge()).mocks.find(r=>r.id===t);return n?{...n}:null}async function Am({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=VT({trackId:t,name:e,cwd:o,width:s}),f=typeof n=="string"&&n.trim();if(!f&&!r)throw new Error("html (string) or image (png) required");if(r&&!xm(r))throw new Error("image must be a PNG");let d=await ge(),p={id:IT().slice(0,8),trackId:t,box:i||null,agent:c||null,name:e.trim(),prompt:l?String(l).slice(0,500):null,group:a?String(a).trim():null,cwd:o,width:u,height:null,dpr:null,state:"queued",source:f?"html":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await At.mkdir(Ea(o),{recursive:!0}),d.mocks.push(p),f?(await At.writeFile(Jt(p,1).html,n),await we(),await se(p,"created"),un(p),Un(p,1,"html"),Sm(()=>vm(p.id,1))):(await Em(p,1,r),await we(),await se(p,"created"),await se(p,"ready",{version:1,width:p.width,height:p.height,dpr:p.dpr}),un(p),Un(p,1,"png")),{...p}}async function _m(t,{html:e,image:n,width:r}={}){let s=(await ge()).mocks.find(c=>c.id===t);if(!s)throw new Error("mock not found");let a=typeof e=="string"&&e.trim();if(!a&&!n)throw new Error("html (string) or image (png) required");if(n&&!xm(n))throw new Error("image must be a PNG");if(r!=null){let c=Number(r);if(!Number.isInteger(c)||c<320||c>4e3)throw new Error("invalid width (320-4000)");s.width=c}let i=s.version+1;return s.version=i,s.updatedAt=Date.now(),s.source=a?"html":"image",await At.mkdir(Ea(s.cwd),{recursive:!0}),a?(s.state="queued",await At.writeFile(Jt(s,i).html,e),await we(),await se(s,"updated",{version:i}),un(s),Un(s,i,"html"),Sm(()=>vm(s.id,i))):(await Em(s,i,n),await we(),await se(s,"updated",{version:i}),await se(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr}),un(s),Un(s,i,"png")),{...s}}async function Em(t,e,n){let r=Zc(n);if(!r)throw new Error("image must be a PNG");let o=Jt(t,e);await At.writeFile(o.png,n),Tm(t,{v:e,source:"image",htmlRel:null,pngRel:Ht.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Im(t){let e=await ge(),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 se(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Rm(t){let e=await ge(),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=Jt(r,o);await At.rm(s.html,{force:!0}).catch(()=>{}),await At.rm(s.png,{force:!0}).catch(()=>{})}return await we(),await se(r,"deleted"),re("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function $m(t){let n=(await ge()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Ht.join(ym,"mock-archive",t.id),o=Ea(t.cwd),s=Ht.join(r,".bostrat","mocks");await At.mkdir(s,{recursive:!0});let a=await At.readdir(o).catch(()=>[]);for(let i of a)await At.copyFile(Ht.join(o,i),Ht.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel]){if(!l)continue;if(!(await At.stat(Ht.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(),un(i)}return await we(),n.length}async function Om(t){let e=await ge(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await we();for(let r of n)re("mocks",r.id).catch(()=>{})}return n.length}async function Mm(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await ge();for(let r of n.mocks){await un(r);for(let o of r.versions||[])o.htmlRel&&await Un(r,o.v,"html"),o.pngRel&&await Un(r,o.v,"png")}return{pushed:n.mocks.length,source:"plane"}}import jo from"node:fs/promises";import Wt from"node:path";import zT from"node:os";import JT from"node:net";import{randomUUID as XT}from"node:crypto";import{execFile as YT}from"node:child_process";import{promisify as ZT}from"node:util";var be=ZT(YT),QT=process.env.BOSTRAT_STATE_DIR||Wt.join(process.env.HOME||zT.tmpdir(),".bostrat"),Bm=Wt.join(QT,"views.json"),[Cm,Nm]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),fn=Number(process.env.BOSTRAT_VIEW_MAX)||3,tA="bostrat-view-",Rr=t=>`${tA}${t}`,Pm=new Set(["bostrat","www","api","views","app"]),Ia=t=>Object.assign(new Error(t),{status:400}),eA=new Set(["npm","pnpm","yarn","bun"]),el="package.json",nA=/[|;&`]|\$\(/,rA=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),oA=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,jm=t=>String(t).trim().split(/\s+/).filter(Boolean),sA=/^[A-Za-z_][A-Za-z0-9_]*=/;function aA(t){for(let e of jm(t))if(!sA.test(e))return e;return null}function Lm(t){let e=jm(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function iA(t,e){let n=Lm(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 cA=t=>jo.stat(t).then(e=>e.isDirectory(),()=>!1),Fm=t=>jo.stat(t).then(e=>e.isFile(),()=>!1),Ra=t=>jo.realpath(t).catch(()=>Wt.resolve(t));async function lA(t,e){if(Wt.resolve(t)===Wt.resolve(e))return!0;let[n,r]=await Promise.all([Ra(t),Ra(e)]);return n===r}async function uA(t){let n=(await jo.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 Fm(Wt.join(t,o.name,el))?`./${o.name}`:null))).filter(Boolean)}async function dA(t,e){let[n,r]=await Promise.all([Ra(t),Ra(e)]),o=Wt.relative(n,r);if(o&&(o.startsWith("..")||Wt.isAbsolute(o)))throw Ia(`serve directory escapes the Track workspace: ${e}`)}async function fA(t,e){if(!await cA(t))throw Ia(`serve directory does not exist: ${t}`);let n=aA(e);if(!n)return;if(!n.includes("/")&&!await rp(n,{loginShell:!0,failOpen:!0}))throw Ia(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(nA.test(e)||rA.has(n)||!eA.has(n)||!Lm(e).includes("run")||oA.test(e))return;let r=iA(t,e);if(await Fm(Wt.join(r,el)))return;let o=await uA(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw Ia(`no ${el} in ${r}${s}`)}var ye=null,Qc=null;async function Be(){return ye||(Qc||=(async()=>{try{ye=JSON.parse(await jo.readFile(Bm,"utf8"))}catch{ye={views:[],adoptWatermarks:{}}}finally{Qc=null}return ye.views||=[],ye.adoptWatermarks||={},ye})(),Qc)}async function ke(){await Lt(Bm,ye)}function De(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var Bo=new Map,ol=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Um=12e3;function sl(t){return Bo.get(t)||null}function Hm(t,{name:e,box:n}={}){let r=Bo.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 Bo.set(t,o),o}function nl(t,e){let n=Bo.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function $a(t,{error:e=null}={}){let n=Bo.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Wm(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 rl(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function pA(t,e){let n=rl(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Pm.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Pm.has(s))return s}}function Lo(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=JT.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 al(t){if(process.platform!=="darwin"){await be("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await be("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 qm(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function mA(t,e){let n=new Set(t.views.map(o=>o.port)),r=qm();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=Cm;o<=Nm;o++)if(!(n.has(o)||r.has(o))&&!await Lo(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Cm}-${Nm}`)}async function hA(t){try{let{stdout:e}=await be(M,N("list-sessions","-F","#{session_name}"));return e.split(`
77
- `).some(n=>n.trim()===Rr(t))}catch{return!1}}async function Fo(t){try{await be(M,N("kill-session","-t",Rr(t)))}catch{}}async function il(t){if(!await Ro())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=Rr(t.slug),n=t.serveCwd??t.cwd,r=Wt.dirname(process.execPath);await be(M,N("new-session","-d","-s",e,"-c",n)),await be(M,N("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await be(M,N("send-keys","-t",e,"Enter")),await be(M,N("send-keys","-t",e,"-l",t.cmd)),await be(M,N("send-keys","-t",e,"Enter"))}async function Dm(t,e=12){try{let{stdout:n}=await be(M,N("capture-pane","-t",Rr(t),"-p"));return String(n||"").trimEnd().split(`
76
+ `)}`:e),this.name="MockRenderError"}},Hn=null,el=null;async function we(){return Hn||(el||=(async()=>{try{Hn=JSON.parse(await Tt.readFile(Sm,"utf8"))}catch{Hn={mocks:[]}}finally{el=null}return Hn.mocks||=[],Hn})(),el)}async function he(){await jt(Sm,Hn)}var fn=t=>Nt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Wn=(t,e,n)=>on("mocks",t.id,e,n,zt(t,e)[n]).catch(()=>{});function oe(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var $a=t=>Ut.join(t,".bostrat","mocks");function zt(t,e){let n=$a(t.cwd);return{html:Ut.join(n,`${t.id}.v${e}.html`),png:Ut.join(n,`${t.id}.v${e}.png`)}}function Ne(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 rl(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var HT=Buffer.from([137,80,78,71,13,10,26,10]);function Am(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(HT)}async function WT(t){let e=Buffer.alloc(24),n=await Tt.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function qT({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 KT(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+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 GT(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 VT({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await NT.launch({executablePath:BT,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new re("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Or})).newPage();a.setDefaultTimeout(Mr),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Mr}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Mr});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await KT(a,i),u=l&&qT({...l,pad:FT*Or,align:Or});u&&(i=await GT(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Tm)throw new re(`capture produced a ${i.length}-byte image`);let c=rl(i);if(!c)throw new re("output is not a PNG");return await Tt.writeFile(e,i),{width:Math.round(c.width/Or),height:Math.round(c.height/Or),dpr:Or,renderMs:Date.now()-r}}catch(s){throw s instanceof re?s:new re(/timeout/i.test(s?.message||"")?`render timed out after ${Mr}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function km({htmlPath:t,outPath:e,width:n}){let r=await Tt.mkdtemp(Ut.join(xm.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await PT(jT,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Mr,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new re(c?.killed?`render timed out after ${Mr}ms`:"firefox exited non-zero",s)}let a=await Tt.stat(e).catch(()=>null);if(!a||a.size<Tm)throw new re(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=rl(await WT(e));if(!i)throw new re("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Tt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function zT(t){if(DT==="firefox")return km(t);try{return await VT(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return km(t);throw e}}var JT=zT;var nl=Promise.resolve();function _m(t){return nl=nl.then(t,t),nl}async function Em(t,e){let n=await we(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await he(),await oe(r,"rendering",{version:e});try{let o=await Ra();if(o<UT)throw new re(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=zt(r,e),a=await JT({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await Tt.rm(s.png,{force:!0}).catch(()=>{});return}Im(r,{v:e,source:"html",htmlRel:Ut.relative(r.cwd,s.html),pngRel:Ut.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await he(),await oe(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),fn(r),Wn(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await he(),await oe(r,"error",{version:e,error:r.error}),fn(r)}}}function Im(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 XT({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?LT:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function qn({trackId:t,group:e}={}){let n=await we(),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 pn(t){let n=(await we()).mocks.find(r=>r.id===t);return n?{...n}:null}async function Rm({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=XT({trackId:t,name:e,cwd:o,width:s}),f=typeof n=="string"&&n.trim();if(!f&&!r)throw new Error("html (string) or image (png) required");if(r&&!Am(r))throw new Error("image must be a PNG");let d=await we(),p={id:OT().slice(0,8),trackId:t,box:i||null,agent:c||null,name:e.trim(),prompt:l?String(l).slice(0,500):null,group:a?String(a).trim():null,cwd:o,width:u,height:null,dpr:null,state:"queued",source:f?"html":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await Tt.mkdir($a(o),{recursive:!0}),d.mocks.push(p),f?(await Tt.writeFile(zt(p,1).html,n),await he(),await oe(p,"created"),fn(p),Wn(p,1,"html"),_m(()=>Em(p.id,1))):(await Om(p,1,r),await he(),await oe(p,"created"),await oe(p,"ready",{version:1,width:p.width,height:p.height,dpr:p.dpr}),fn(p),Wn(p,1,"png")),{...p}}async function $m(t,{html:e,image:n,width:r}={}){let s=(await we()).mocks.find(c=>c.id===t);if(!s)throw new Error("mock not found");let a=typeof e=="string"&&e.trim();if(!a&&!n)throw new Error("html (string) or image (png) required");if(n&&!Am(n))throw new Error("image must be a PNG");if(r!=null){let c=Number(r);if(!Number.isInteger(c)||c<320||c>4e3)throw new Error("invalid width (320-4000)");s.width=c}let i=s.version+1;return s.version=i,s.updatedAt=Date.now(),s.source=a?"html":"image",await Tt.mkdir($a(s.cwd),{recursive:!0}),a?(s.state="queued",await Tt.writeFile(zt(s,i).html,e),await he(),await oe(s,"updated",{version:i}),fn(s),Wn(s,i,"html"),_m(()=>Em(s.id,i))):(await Om(s,i,n),await he(),await oe(s,"updated",{version:i}),await oe(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr}),fn(s),Wn(s,i,"png")),{...s}}async function Om(t,e,n){let r=rl(n);if(!r)throw new Error("image must be a PNG");let o=zt(t,e);await Tt.writeFile(o.png,n),Im(t,{v:e,source:"image",htmlRel:null,pngRel:Ut.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Mm(t){let e=await we(),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 oe(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Cm(t){let e=await we(),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=zt(r,o);await Tt.rm(s.html,{force:!0}).catch(()=>{}),await Tt.rm(s.png,{force:!0}).catch(()=>{})}return await he(),await oe(r,"deleted"),ne("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Nm(t){let n=(await we()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Ut.join(vm,"mock-archive",t.id),o=$a(t.cwd),s=Ut.join(r,".bostrat","mocks");await Tt.mkdir(s,{recursive:!0});let a=await Tt.readdir(o).catch(()=>[]);for(let i of a)await Tt.copyFile(Ut.join(o,i),Ut.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel]){if(!l)continue;if(!(await Tt.stat(Ut.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(),fn(i)}return await he(),n.length}async function Pm(t){let e=await we(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await he();for(let r of n)ne("mocks",r.id).catch(()=>{})}return n.length}async function Dm(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Rt("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await we();for(let r of n.mocks){await fn(r);for(let o of r.versions||[])o.htmlRel&&await Wn(r,o.v,"html"),o.pngRel&&await Wn(r,o.v,"png")}return{pushed:n.mocks.length,source:"plane"}}import Uo from"node:fs/promises";import Ht from"node:path";import YT from"node:os";import ZT from"node:net";import{randomUUID as QT}from"node:crypto";import{execFile as tA}from"node:child_process";import{promisify as eA}from"node:util";var ye=eA(tA),nA=process.env.BOSTRAT_STATE_DIR||Ht.join(process.env.HOME||YT.tmpdir(),".bostrat"),Um=Ht.join(nA,"views.json"),[Bm,jm]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),mn=Number(process.env.BOSTRAT_VIEW_MAX)||3,rA="bostrat-view-",Cr=t=>`${rA}${t}`,Lm=new Set(["bostrat","www","api","views","app"]),Oa=t=>Object.assign(new Error(t),{status:400}),oA=new Set(["npm","pnpm","yarn","bun"]),al="package.json",sA=/[|;&`]|\$\(/,aA=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),iA=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Hm=t=>String(t).trim().split(/\s+/).filter(Boolean),cA=/^[A-Za-z_][A-Za-z0-9_]*=/;function lA(t){for(let e of Hm(t))if(!cA.test(e))return e;return null}function Wm(t){let e=Hm(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function uA(t,e){let n=Wm(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Ht.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Ht.resolve(t,n[r+1])}return t}var dA=t=>Uo.stat(t).then(e=>e.isDirectory(),()=>!1),qm=t=>Uo.stat(t).then(e=>e.isFile(),()=>!1),Ma=t=>Uo.realpath(t).catch(()=>Ht.resolve(t));async function fA(t,e){if(Ht.resolve(t)===Ht.resolve(e))return!0;let[n,r]=await Promise.all([Ma(t),Ma(e)]);return n===r}async function pA(t){let n=(await Uo.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 qm(Ht.join(t,o.name,al))?`./${o.name}`:null))).filter(Boolean)}async function mA(t,e){let[n,r]=await Promise.all([Ma(t),Ma(e)]),o=Ht.relative(n,r);if(o&&(o.startsWith("..")||Ht.isAbsolute(o)))throw Oa(`serve directory escapes the Track workspace: ${e}`)}async function hA(t,e){if(!await dA(t))throw Oa(`serve directory does not exist: ${t}`);let n=lA(e);if(!n)return;if(!n.includes("/")&&!await ap(n,{loginShell:!0,failOpen:!0}))throw Oa(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(sA.test(e)||aA.has(n)||!oA.has(n)||!Wm(e).includes("run")||iA.test(e))return;let r=uA(t,e);if(await qm(Ht.join(r,al)))return;let o=await pA(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw Oa(`no ${al} in ${r}${s}`)}var ge=null,ol=null;async function Be(){return ge||(ol||=(async()=>{try{ge=JSON.parse(await Uo.readFile(Um,"utf8"))}catch{ge={views:[],adoptWatermarks:{}}}finally{ol=null}return ge.views||=[],ge.adoptWatermarks||={},ge})(),ol)}async function be(){await jt(Um,ge)}function De(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var Fo=new Map,ll=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Km=12e3;function ul(t){return Fo.get(t)||null}function Gm(t,{name:e,box:n}={}){let r=Fo.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 Fo.set(t,o),o}function il(t,e){let n=Fo.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ca(t,{error:e=null}={}){let n=Fo.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Vm(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 cl(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function wA(t,e){let n=cl(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Lm.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Lm.has(s))return s}}function Ho(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=ZT.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 dl(t){if(process.platform!=="darwin"){await ye("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await ye("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 zm(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function gA(t,e){let n=new Set(t.views.map(o=>o.port)),r=zm();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=Bm;o<=jm;o++)if(!(n.has(o)||r.has(o))&&!await Ho(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Bm}-${jm}`)}async function yA(t){try{let{stdout:e}=await ye(M,N("list-sessions","-F","#{session_name}"));return e.split(`
77
+ `).some(n=>n.trim()===Cr(t))}catch{return!1}}async function Wo(t){try{await ye(M,N("kill-session","-t",Cr(t)))}catch{}}async function fl(t){if(!await Co())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=Cr(t.slug),n=t.serveCwd??t.cwd,r=Ht.dirname(process.execPath);await ye(M,N("new-session","-d","-s",e,"-c",n)),await ye(M,N("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await ye(M,N("send-keys","-t",e,"Enter")),await ye(M,N("send-keys","-t",e,"-l",t.cmd)),await ye(M,N("send-keys","-t",e,"Enter"))}async function Fm(t,e=12){try{let{stdout:n}=await ye(M,N("capture-pane","-t",Cr(t),"-p"));return String(n||"").trimEnd().split(`
78
78
  `).filter(Boolean).slice(-e).join(`
79
- `)}catch{return""}}var Pe=new Map,wA=12e4;async function gA(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await hA(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Dm(t.slug)||"server exited before listening"),Pe.delete(t.id),await De(t,t.state,t.error?{error:t.error}:{}),!0}if(await Lo(t.port))return Pe.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await De(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>wA?(t.state="error",t.error=await Dm(t.slug)||"never started listening",await De(t,"error",{error:t.error}),!0):!1;let o=(Pe.get(t.id)||0)+1;return Pe.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var tl=!1;async function Oa(){if(!tl){tl=!0;try{let t=await Be(),e=!1;for(let n of t.views)await gA(n)&&(e=!0);e&&await ke()}finally{tl=!1}}}async function Ma({trackId:t}={}){await Oa();let e=await Be();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function Km(t){let n=(await Be()).views.find(r=>r.id===t);return n?{...n}:null}function cl(t){return ye&&ye.views.find(e=>e.slug===t)||null}async function ll({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 dA(r,u),await fA(u,n)},d=await Be();if(i!=="detected"){let w=rl(e),y=String(n).trim(),b=d.views.find(S=>S.trackId===t&&(S.slug===w||rl(S.name)===w||S.cmd===y&&Wt.resolve(S.serveCwd??S.cwd)===u));if(b)return yA(d,b,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=fn)throw new Error(`view cap reached (${fn} live) \u2014 stop one first`);let m={id:XT().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:pA(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await mA(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await il(m),d.views.push(m),await ke(),await De(m,"created",{origin:i}),nl(t,m.id),i==="detected"&&await Lo(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await ke(),await De(m,"live")),{...m}}async function yA(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 lA(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Lo(e.port))return await ke(),nl(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>=fn)throw new Error(`view cap reached (${fn} live) \u2014 stop one first`);return e.origin==="detected"?(await al(e.port),e.origin="agent"):await Fo(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Pe.delete(e.id),await il(e),await ke(),await De(e,"created",{restarted:!0,reused:!0}),nl(e.trackId,e.id),{...e,reused:!0}}async function Gm(t){let n=(await Be()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await al(n.port):await Fo(n.slug),n.state!=="stopped"&&(n.state="stopped",Pe.delete(n.id),await ke(),await De(n,"stopped")),{...n}}async function Vm(t){let e=await Be(),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>=fn)throw new Error(`view cap reached (${fn} live) \u2014 stop one first`);return await Fo(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Pe.delete(n.id),await il(n),await ke(),await De(n,"created",{restarted:!0}),{...n}}async function zm(t){let e=await Be(),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 al(r.port):await Fo(r.slug),e.views.splice(n,1),Pe.delete(r.id),await ke(),await De(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function Jm(t){let e=await Be(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Fo(r.slug),Pe.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await ke()),n.length}var bA=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function Xm(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await Be(),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 ke()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(bA)){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(!qm().has(c)&&!n.views.some(l=>l.port===c)&&await Lo(c))try{i.push(await ll({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 Fe from"node:fs/promises";import Ue from"node:path";import kA from"node:os";import{randomUUID as xA}from"node:crypto";var Wn=process.env.BOSTRAT_STATE_DIR||Ue.join(process.env.HOME||kA.tmpdir(),".bostrat"),Zm=Ue.join(Wn,"runbooks.json"),Ca=Ue.join(Wn,"runbooks"),Ym=256*1024,SA=[{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 Uo(t){return SA.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var je=null,ul=null;async function He(){return je||(ul||=(async()=>{let t=!0;try{je=JSON.parse(await Fe.readFile(Zm,"utf8"))}catch{je={runbooks:[]},t=!1}finally{ul=null}return je.runbooks||=[],!t&&wt()&&await vA().catch(()=>{}),je})(),ul)}async function qn(){await Lt(Zm,je)}var Ho=t=>Promise.resolve(t).catch(()=>{});function Na(t){return Ho(Dt("runbooks",{...t},{trackId:t.trackId}))}async function vA(){let{records:t,source:e}=await Ot("runbooks");if(!(e!=="plane"||!t.length)){await Fe.mkdir(Ca,{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 na("runbooks",r.id,o.v,"md");typeof s=="string"&&await Fe.writeFile(Le(r,o.v),s).catch(()=>{})}je.runbooks.push(r)}je.runbooks.length&&await qn()}}function $r(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function Le(t,e){return Ue.join(Ca,`${t.id}.v${e}.md`)}function Qm(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 th(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>Ym)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${Ym/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Uo(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 TA(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Pa({trackId:t,status:e}={}){let n=await He(),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 dl(t){let n=(await He()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function eh({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=th(r),c=await He(),l={id:xA().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 Fe.mkdir(Ca,{recursive:!0}),await Fe.writeFile(Le(l,1),r),l.versions.push({v:1,mdRel:Ue.relative(Wn,Le(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await qn(),await $r(l,"created",{version:1}),Na(l),Ho(rn("runbooks",l.id,1,"md",Le(l,1))),{...l}}async function nh(t,{md:e,note:n}={}){let o=(await He()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");TA(o,"update");let s=th(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Fe.mkdir(Ca,{recursive:!0}),await Fe.writeFile(Le(o,a),e),o.versions.push({v:a,mdRel:Ue.relative(Wn,Le(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await qn(),await $r(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Na(o),Ho(rn("runbooks",o.id,a,"md",Le(o,a))),{...o}}async function rh(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await He()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await qn(),await $r(r,e==="done"?"done":"reopened"),Na(r),{...r})}async function oh(t){let e=await He(),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 Fe.rm(Ue.join(Wn,o.mdRel),{force:!0}).catch(()=>{});return await qn(),r.trackDeletedAt||await $r(r,"deleted"),Ho(re("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function sh(t){let n=(await He()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await $r(r,"orphaned"),r.cwd=null,Na(r);return n.length&&await qn(),n.length}async function ah(t){let e=await He(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await $r(r,"deleted");for(let o of r.versions||[])await Fe.rm(Ue.join(Wn,o.mdRel),{force:!0}).catch(()=>{});Ho(re("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await qn()),n.length}async function ih(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await He();for(let r of n.runbooks){await Dt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await rn("runbooks",r.id,o.v,"md",Ue.join(Wn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import Ko from"node:fs/promises";import xh from"node:path";import t_ from"node:os";import{randomUUID as Sh}from"node:crypto";import AA from"node:fs/promises";import ch from"node:path";import _A from"node:os";var lh=()=>ch.join(process.env.BOSTRAT_STATE_DIR||ch.join(process.env.HOME||_A.tmpdir(),".bostrat"),"integrations.json");async function We(){try{return JSON.parse(await AA.readFile(lh(),"utf8"))||{}}catch{return{}}}var Da=["linked","prOpened","prMerged","create"],fl=["prompt","auto","off"],EA={create:["prompt","off"]},uh=t=>EA[t]??fl;function Ba(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of Da){let s=uh(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 Kn(){let t=await We(),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:Ba(t),defaultTeam:n}:null}async function ja(){let t=await We(),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:Ba(t)}}async function pl(){return Ba(await We())}async function dh(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${Da.join(", ")}`);for(let[r,o]of e){if(!Da.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Da.join(", ")}`);let s=uh(r);if(!s.includes(o))throw new Error(s.length<fl.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${fl.join(", ")}`)}let n=await We();return n.ticketFlow={...Ba(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Lt(lh(),n,{mode:384}),n.ticketFlow}async function Wo(){let t=(await We()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function IA(){let t=(await We()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var RA={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function La(){let e=(await We()).voiceChat||{},n=e.provider||"openai",r=RA[n];if(!r)return null;let o=n==="openai"?await IA():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function fh(){let t=(await We()).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 Gn(){let t=(await We()).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 hl={};lf(hl,{createComment:()=>jA,findIssueUrl:()=>CA,issueStates:()=>UA,mdToAdf:()=>hh,moveIssue:()=>HA,parseIssueRef:()=>MA,resolveIssue:()=>NA,uploadImage:()=>LA,upsertCard:()=>PA,whoami:()=>ml});var Or=t=>String(t.baseUrl||"").replace(/\/$/,""),mh=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Vn(t,e,n,r){let o=await fetch(`${Or(t)}${n}`,{method:e,headers:{authorization:mh(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 $A=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,OA=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function MA(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match($A);return r?r[1].toUpperCase():null}let n=e.match(OA);return n?n[1]:null}function CA(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 NA(t,e){let n;try{n=await Vn(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:`${Or(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function PA(t,e,{url:n,title:r,subtitle:o}){await Vn(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 DA=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function BA(t){let e=[],n=0;for(let r of t.matchAll(DA)){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 hh(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
80
- `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...BA(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 jA(t,e,n){let r=await Vn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:hh(n)});return r?.id?`${Or(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function ml(t){let e=await Vn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Or(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function LA(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(`${Or(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:mh(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??`${Or(t)}/rest/api/3/attachment/content/${c.id}`}var FA={new:"unstarted",indeterminate:"started",done:"completed"},ph=t=>FA[t?.key]??"started";async function UA(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([Vn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),Vn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:ph(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:ph(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function HA(t,e,n){await Vn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var bl={};lf(bl,{createComment:()=>XA,createIssue:()=>yl,createProject:()=>gl,findIssueUrl:()=>VA,issueStates:()=>ZA,moveIssue:()=>QA,parseIssueRef:()=>GA,resolveIssue:()=>zA,teams:()=>Mr,uploadImage:()=>YA,upsertCard:()=>JA,whoami:()=>wl});var WA=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function xe(t,e,n={}){let r=await fetch(WA(),{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 qA=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,KA=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function GA(t){let e=String(t||"").trim(),n=e.match(qA);if(n)return n[1].toUpperCase();let r=e.match(KA);return r?r[1]:null}function VA(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 zA(t,e){let r=(await xe(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 JA(t,e,{url:n,title:r,subtitle:o}){let s=await xe(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 XA(t,e,n){let r=await xe(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 wl(t){let e=await xe(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 YA(t,{filename:e,contentType:n,buffer:r}){let o=await xe(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
79
+ `)}catch{return""}}var Pe=new Map,bA=12e4;async function kA(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await yA(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Fm(t.slug)||"server exited before listening"),Pe.delete(t.id),await De(t,t.state,t.error?{error:t.error}:{}),!0}if(await Ho(t.port))return Pe.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await De(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>bA?(t.state="error",t.error=await Fm(t.slug)||"never started listening",await De(t,"error",{error:t.error}),!0):!1;let o=(Pe.get(t.id)||0)+1;return Pe.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var sl=!1;async function Na(){if(!sl){sl=!0;try{let t=await Be(),e=!1;for(let n of t.views)await kA(n)&&(e=!0);e&&await be()}finally{sl=!1}}}async function Pa({trackId:t}={}){await Na();let e=await Be();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function Jm(t){let n=(await Be()).views.find(r=>r.id===t);return n?{...n}:null}function pl(t){return ge&&ge.views.find(e=>e.slug===t)||null}async function ml({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=Ht.resolve(r,o??"."),f=i==="detected"?null:async()=>{await mA(r,u),await hA(u,n)},d=await Be();if(i!=="detected"){let w=cl(e),y=String(n).trim(),k=d.views.find(v=>v.trackId===t&&(v.slug===w||cl(v.name)===w||v.cmd===y&&Ht.resolve(v.serveCwd??v.cwd)===u));if(k)return xA(d,k,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=mn)throw new Error(`view cap reached (${mn} live) \u2014 stop one first`);let m={id:QT().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:wA(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await gA(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await fl(m),d.views.push(m),await be(),await De(m,"created",{origin:i}),il(t,m.id),i==="detected"&&await Ho(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await be(),await De(m,"live")),{...m}}async function xA(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 fA(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Ho(e.port))return await be(),il(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>=mn)throw new Error(`view cap reached (${mn} live) \u2014 stop one first`);return e.origin==="detected"?(await dl(e.port),e.origin="agent"):await Wo(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Pe.delete(e.id),await fl(e),await be(),await De(e,"created",{restarted:!0,reused:!0}),il(e.trackId,e.id),{...e,reused:!0}}async function Xm(t){let n=(await Be()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await dl(n.port):await Wo(n.slug),n.state!=="stopped"&&(n.state="stopped",Pe.delete(n.id),await be(),await De(n,"stopped")),{...n}}async function Ym(t){let e=await Be(),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>=mn)throw new Error(`view cap reached (${mn} live) \u2014 stop one first`);return await Wo(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Pe.delete(n.id),await fl(n),await be(),await De(n,"created",{restarted:!0}),{...n}}async function Zm(t){let e=await Be(),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 dl(r.port):await Wo(r.slug),e.views.splice(n,1),Pe.delete(r.id),await be(),await De(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function Qm(t){let e=await Be(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Wo(r.slug),Pe.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await be()),n.length}var vA=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function th(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await Be(),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 be()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(vA)){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(!zm().has(c)&&!n.views.some(l=>l.port===c)&&await Ho(c))try{i.push(await ml({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 Fe from"node:fs/promises";import Ue from"node:path";import SA from"node:os";import{randomUUID as TA}from"node:crypto";var Kn=process.env.BOSTRAT_STATE_DIR||Ue.join(process.env.HOME||SA.tmpdir(),".bostrat"),nh=Ue.join(Kn,"runbooks.json"),Da=Ue.join(Kn,"runbooks"),eh=256*1024,AA=[{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 qo(t){return AA.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var je=null,hl=null;async function He(){return je||(hl||=(async()=>{let t=!0;try{je=JSON.parse(await Fe.readFile(nh,"utf8"))}catch{je={runbooks:[]},t=!1}finally{hl=null}return je.runbooks||=[],!t&&ht()&&await _A().catch(()=>{}),je})(),hl)}async function Gn(){await jt(nh,je)}var Ko=t=>Promise.resolve(t).catch(()=>{});function Ba(t){return Ko(Nt("runbooks",{...t},{trackId:t.trackId}))}async function _A(){let{records:t,source:e}=await Rt("runbooks");if(!(e!=="plane"||!t.length)){await Fe.mkdir(Da,{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 aa("runbooks",r.id,o.v,"md");typeof s=="string"&&await Fe.writeFile(Le(r,o.v),s).catch(()=>{})}je.runbooks.push(r)}je.runbooks.length&&await Gn()}}function Nr(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function Le(t,e){return Ue.join(Da,`${t.id}.v${e}.md`)}function rh(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 oh(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>eh)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${eh/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=qo(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 EA(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function ja({trackId:t,status:e}={}){let n=await He(),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 wl(t){let n=(await He()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function sh({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=oh(r),c=await He(),l={id:TA().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 Fe.mkdir(Da,{recursive:!0}),await Fe.writeFile(Le(l,1),r),l.versions.push({v:1,mdRel:Ue.relative(Kn,Le(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Gn(),await Nr(l,"created",{version:1}),Ba(l),Ko(on("runbooks",l.id,1,"md",Le(l,1))),{...l}}async function ah(t,{md:e,note:n}={}){let o=(await He()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");EA(o,"update");let s=oh(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Fe.mkdir(Da,{recursive:!0}),await Fe.writeFile(Le(o,a),e),o.versions.push({v:a,mdRel:Ue.relative(Kn,Le(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Gn(),await Nr(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Ba(o),Ko(on("runbooks",o.id,a,"md",Le(o,a))),{...o}}async function ih(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await He()).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 Gn(),await Nr(r,e==="done"?"done":"reopened"),Ba(r),{...r})}async function ch(t){let e=await He(),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 Fe.rm(Ue.join(Kn,o.mdRel),{force:!0}).catch(()=>{});return await Gn(),r.trackDeletedAt||await Nr(r,"deleted"),Ko(ne("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function lh(t){let n=(await He()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Nr(r,"orphaned"),r.cwd=null,Ba(r);return n.length&&await Gn(),n.length}async function uh(t){let e=await He(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Nr(r,"deleted");for(let o of r.versions||[])await Fe.rm(Ue.join(Kn,o.mdRel),{force:!0}).catch(()=>{});Ko(ne("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Gn()),n.length}async function dh(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Rt("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await He();for(let r of n.runbooks){await Nt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await on("runbooks",r.id,o.v,"md",Ue.join(Kn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import zo from"node:fs/promises";import Ah from"node:path";import r_ from"node:os";import{randomUUID as _h}from"node:crypto";import IA from"node:fs/promises";import fh from"node:path";import RA from"node:os";var ph=()=>fh.join(process.env.BOSTRAT_STATE_DIR||fh.join(process.env.HOME||RA.tmpdir(),".bostrat"),"integrations.json");async function We(){try{return JSON.parse(await IA.readFile(ph(),"utf8"))||{}}catch{return{}}}var La=["linked","prOpened","prMerged","create"],gl=["prompt","auto","off"],$A={create:["prompt","off"]},mh=t=>$A[t]??gl;function Fa(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of La){let s=mh(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 Vn(){let t=await We(),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:Fa(t),defaultTeam:n}:null}async function Ua(){let t=await We(),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:Fa(t)}}async function yl(){return Fa(await We())}async function hh(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${La.join(", ")}`);for(let[r,o]of e){if(!La.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${La.join(", ")}`);let s=mh(r);if(!s.includes(o))throw new Error(s.length<gl.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${gl.join(", ")}`)}let n=await We();return n.ticketFlow={...Fa(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await jt(ph(),n,{mode:384}),n.ticketFlow}async function Go(){let t=(await We()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function OA(){let t=(await We()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var MA={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Ha(){let e=(await We()).voiceChat||{},n=e.provider||"openai",r=MA[n];if(!r)return null;let o=n==="openai"?await OA():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function wh(){let t=(await We()).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 zn(){let t=(await We()).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 kl={};ff(kl,{createComment:()=>UA,findIssueUrl:()=>DA,issueStates:()=>qA,mdToAdf:()=>bh,moveIssue:()=>KA,parseIssueRef:()=>PA,resolveIssue:()=>BA,uploadImage:()=>HA,upsertCard:()=>jA,whoami:()=>bl});var Pr=t=>String(t.baseUrl||"").replace(/\/$/,""),yh=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Jn(t,e,n,r){let o=await fetch(`${Pr(t)}${n}`,{method:e,headers:{authorization:yh(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 CA=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,NA=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function PA(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(CA);return r?r[1].toUpperCase():null}let n=e.match(NA);return n?n[1]:null}function DA(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 BA(t,e){let n;try{n=await Jn(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:`${Pr(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function jA(t,e,{url:n,title:r,subtitle:o}){await Jn(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 LA=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function FA(t){let e=[],n=0;for(let r of t.matchAll(LA)){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 bh(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
80
+ `).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...FA(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 UA(t,e,n){let r=await Jn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:bh(n)});return r?.id?`${Pr(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function bl(t){let e=await Jn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Pr(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function HA(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(`${Pr(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:yh(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??`${Pr(t)}/rest/api/3/attachment/content/${c.id}`}var WA={new:"unstarted",indeterminate:"started",done:"completed"},gh=t=>WA[t?.key]??"started";async function qA(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([Jn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),Jn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:gh(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:gh(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function KA(t,e,n){await Jn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var Tl={};ff(Tl,{createComment:()=>QA,createIssue:()=>Sl,createProject:()=>vl,findIssueUrl:()=>XA,issueStates:()=>e_,moveIssue:()=>n_,parseIssueRef:()=>JA,resolveIssue:()=>YA,teams:()=>Dr,uploadImage:()=>t_,upsertCard:()=>ZA,whoami:()=>xl});var GA=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function ke(t,e,n={}){let r=await fetch(GA(),{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 VA=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,zA=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function JA(t){let e=String(t||"").trim(),n=e.match(VA);if(n)return n[1].toUpperCase();let r=e.match(zA);return r?r[1]:null}function XA(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 YA(t,e){let r=(await ke(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 ZA(t,e,{url:n,title:r,subtitle:o}){let s=await ke(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 QA(t,e,n){let r=await ke(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 xl(t){let e=await ke(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 t_(t,{filename:e,contentType:n,buffer:r}){let o=await ke(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
81
81
  fileUpload(contentType: $contentType, filename: $filename, size: $size) {
82
82
  success
83
83
  uploadFile { uploadUrl assetUrl headers { key value } }
84
84
  }
85
- }`,{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 ZA(t,e){let n=await xe(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 QA(t,e,n){if(!(await xe(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 Mr(t){return((await xe(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var wh=255;async function gl(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>wh,a=s?`${o.slice(0,wh-1).trimEnd()}\u2026`:o,i=await xe(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 yl(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await xe(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 vh={linear:{api:bl,config:Kn},jira:{api:hl,config:ja}};async function _l(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(vh)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function zn(t){let e=vh[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function El(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 Th=()=>process.env.BOSTRAT_STATE_DIR||xh.join(process.env.HOME||t_.tmpdir(),".bostrat"),vl=()=>xh.join(Th(),"tickets.json"),Ah=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Fa=t=>`${Ah()}/track/${t}`,pn=null,kl=null,xl=Promise.resolve();async function Vo(){return pn||(kl??=Ko.readFile(vl(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(pn=t,!e&&wt()){let{records:n,source:r}=await Ot("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;pn[s]=i}Object.keys(pn).length&&await Tl().catch(()=>{})}}return pn}).finally(()=>kl=null),kl)}async function Tl(){let t=JSON.stringify({tickets:pn},null,2),e=`${vl()}.${process.pid}.tmp`;await Ko.mkdir(Th(),{recursive:!0}),await Ko.writeFile(e,t),await Ko.rename(e,vl())}async function mn(t=null){if(xl=xl.then(Tl,Tl),await xl.catch(()=>{}),!t)return;let e=pn?.[t];e?Dt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):re("tickets",t).catch(()=>{})}async function Se(t){return(await Vo())[t]??null}async function Ua(){return{...await Vo()}}async function _h(){return(await _l()).length>0}async function Cr(t,e,{source:n="api"}={}){let r=await _l();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 Vo())[t.id]=c,await mn(t.id),await I({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Go(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 qo(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Rh(t.id,c,"linked"),c}async function Il(t){let e=await Vo();return e[t]?(delete e[t],await mn(t),!0):!1}var Al=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Eh(t,e){if(!t.track?.cwd)return null;let n=he(await L({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function e_(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Al(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${Al(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function n_(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(Al(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Go(t,e){let n=await zn(e);if(!n)return;let r=await Eh(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:Fa(t),title:`Bostrat Track: ${e.track.name}`,subtitle:n_(e,r)})}function Ha(t){let e=new URL(Ah());return`${e.protocol}//${t}.${e.host}/`}var r_={linked:"linked",opened:"prOpened",merged:"prMerged"};function o_(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 Ih(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await mn(t),await I({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Rh(t,e,n){let r=await zn(e);if(!r)return;let o=r.cfg.ticketFlow?.[r_[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=El(s,a),c=o_(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 Ih(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await I({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Sh(),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 $h(t){let e=await Se(t);if(!e)throw new Error("no ticket linked to this Track");let n=await zn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=El(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await mn(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 Rl(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await Se(t);if(!r)throw new Error("no ticket linked to this Track");let o=await zn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=El(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 mn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Ih(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Oh(t,e){let n=await Se(t);if(!n)throw new Error("no ticket linked to this Track");let r=await zn(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await I({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function Mh(t,e,n){if(!e)throw new Error("offerId required");let r=await L({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 Rl(t.id,o.payload.to?.id,{mode:"offer"})),await I({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var gh=50;async function Ch(){let t=await Kn();if(!t)throw new Error("no ticketing integration configured");return Mr(t)}function s_(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>gh)throw new Error(`too many issues (${n.length}); cap is ${gh} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function a_(t,e,n,r){let o=r??await Mr(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 Se(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 Nh(t,e){let n=await Kn();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=s_(e),o=await Mr(n),s=await a_(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Sh();return await I({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function Ph(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await L({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await I({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await Kn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Mr(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let m=await gl(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 w=await yl(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}await I({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await I({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await Se(t.id)&&(p=await Cr(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function qo(t,e){let n=await zn(t);n&&await n.api.createComment(n.cfg,t,e)}async function i_(t,e,n){let r=await zn(t);if(!r)return null;let o=await dn(e);if(!o)return null;let s=Ne(o,n);if(!s)return null;let a=await Ko.readFile(Jt(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 yh=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Sl=new Map;function c_(t,e){if(yh()<=0)return Go(t,e);if(Sl.has(t))return;let n=setTimeout(()=>{Sl.delete(t),Se(t).then(r=>r?Go(t,r):null).catch(()=>{})},yh());n.unref?.(),Sl.set(t,n)}var bh=new Map;async function kh(t,e,n){let r=bh.get(t)??0;Date.now()-r<600*1e3||(bh.set(t,Date.now()),await I({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function l_(t,{resolveTrack:e=null}={}){let n=await Se(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await _l())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 Cr(s,o,{source:"chat"})}catch(a){await kh(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 mn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await mn(t.trackId),await Go(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 mn(t.trackId),await Go(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Eh(n,t.trackId).catch(()=>null):null;await qo(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
86
-
87
- Agent cost for this Track: ${e_(o)}.`:"")),await Rh(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await i_(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await qo(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
88
-
89
- ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await qo(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 qo(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Ha(r.slug)}
90
- _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await c_(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await kh(t.trackId,s?.cwd??null,o)}}function Dh({resolveTrack:t}){Ut.on("event",e=>{l_(e,{resolveTrack:t}).catch(()=>{})})}async function Bh(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Vo(),r=0;for(let[o,s]of Object.entries(n))await Dt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import u_ from"node:crypto";import Wa from"node:fs/promises";import jh from"node:path";import d_ from"node:os";var Lh=process.env.BOSTRAT_STATE_DIR||jh.join(process.env.HOME||d_.tmpdir(),".bostrat"),Ml=jh.join(Lh,"pr-links.json"),V=(t,e)=>`${t}#${Number(e)}`,Fh=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=u_.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},hn=null,$l=null,Ol=Promise.resolve();async function kt(){return hn||($l??=Wa.readFile(Ml,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(hn=t,!e&&wt()){let{records:n,source:r}=await Ot("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;hn[V(i.repo,i.number)]=i}Object.keys(hn).length&&await Cl().catch(()=>{})}}return hn}).finally(()=>$l=null),$l)}async function Cl(){let t=JSON.stringify({links:hn},null,2),e=`${Ml}.${process.pid}.tmp`;await Wa.mkdir(Lh,{recursive:!0}),await Wa.writeFile(e,t),await Wa.rename(e,Ml)}async function It(t){Ol=Ol.then(Cl,Cl),await Ol.catch(()=>{});let e=hn?.[t];e&&Dt("prlinks",{id:Fh(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var f_=new Set(["api","observed","reconciled"]);async function Xt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await kt(),c=V(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:f_.has(a),firstSeen:Date.now()},await It(V(t,e)),{link:i[c],created:!0})}async function zo(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||n.announced?null:(n.announced=!0,await It(V(t,e)),n)}async function Bt(t,e){return!t||e==null?null:(await kt())[V(t,e)]??null}async function wn(t,e,n){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await It(V(t,e)),r):null}async function Uh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await kt())[V(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await It(V(t,e)),o):null}async function Hh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await kt())[V(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await It(V(t,e)),o):null}async function qa(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await It(V(t,e)),r)}async function Ka(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await It(V(t,e)),n)}async function Ga(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await It(V(t,e)),r)}async function Va(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await It(V(t,e)),n)}async function Wh(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(r.baseFoldAt=n,await It(V(t,e)),r):null}async function qh(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await It(V(t,e)),n)}async function Kh(t,e,n){if(!t||e==null||!n)return null;let r=(await kt())[V(t,e)];return r?(r.lastHeadSha=n,await It(V(t,e)),r):null}async function Jo(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await It(V(t,e)),r):null}async function Jn(t,e,n=!0){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await It(V(t,e)),r):null}async function Xo(t,e,n=!0){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await It(V(t,e)),r):null}async function Gh(t,e,n){if(!t||e==null)return null;let r=(await kt())[V(t,e)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await It(V(t,e)),r}async function Yo(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await It(V(t,e)),n)}var p_=new Set(["merged","closed","lost"]);async function Vh(){return Object.values(await kt()).filter(t=>!p_.has(t.state))}async function ve(){return Object.values(await kt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function zh(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await kt(),r=0;for(let[o,s]of Object.entries(n))await Dt("prlinks",{id:Fh(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Xh=t=>Math.round(t*100)/100;function m_(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():Jh.basename(e.base)}))}function Yh(t){let e=new Map;for(let n of t||[])for(let r of m_(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function za(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 Nl=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function h_(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 Pl(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(Nl(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Nl(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(`${Nl(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=he(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?h_(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function w_(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 Zh(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=za(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:Xh(l),firstActivityAt:c,lastActivityAt:i}}async function Qh(t){let e=Yh(t),n=await ve().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 L(i).catch(()=>[]);r.set(i.id,he(c).totalUsd)}let s=[...e.values()].map(a=>Zh(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function tw(t,e){let r=Yh(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([ve().catch(()=>[]),Hn().catch(()=>[]),Ma().catch(()=>[]),Pa().catch(()=>[]),Ua().catch(()=>({}))]),l=new Map,u=[],f=(d,p)=>(za(p)||p.createdAt||0)-(za(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let p=await L(d).catch(()=>[]),m=he(p);l.set(d.id,m.totalUsd);let w=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:w_(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||Jh.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:w?{provider:w.provider,key:w.key,url:w.url??null}:null,usd:Xh(m.totalUsd),lastActivityAt:za(d)})}return{...Zh(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as g_}from"node:child_process";import{promisify as y_}from"node:util";import{randomUUID as b_}from"node:crypto";var Nr=y_(g_),Ja=t=>new Promise(e=>setTimeout(e,t)),k_=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,ew=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,x_=250,S_=400,nw={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 Dl(t,e){let n=Date.now()+e;for(;;){if(await kr(t))return!0;if(Date.now()>n)return!1;await Ja(x_)}}async function v_(t,e){let n=nw[e]||nw.default;n.escape&&(await vt(t,["Escape"],{kind:"swap-exit"}),await Ja(150)),n.wipe&&await la(t,400,{kind:"swap-exit"}),await ua(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Ce(e).submitDelayMs||0;await Ja(Math.max(r,250)),await vt(t,["Enter"],{kind:"swap-exit"})}async function T_(t,e){try{let{stdout:n}=await Nr(M,N("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function rw(t){await Nr(M,N("respawn-pane","-k","-t",t))}async function Bl(t,e={}){if(!bt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await rt(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||on(n),a=Number(e.exitTimeoutMs)||k_;await G({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await kr(t)&&(await v_(t,o),!await Dl(t,a)&&(i=!0,await G({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await rw(t),!await Dl(t,ew))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&Dn(n),l=c?Gs():null,u=await T_(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Nr(M,N("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Nr(M,N("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?oa(y,l):y}if(f&&!i&&(await rw(t),!await Dl(t,ew)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||b_(),m=d?n:Rc(n,p),w=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await tn(r.cwd),s==="codex"&&await dr(r.cwd),s==="cursor"&&await fr(r.cwd),await jn(t),await Nr(M,N("set-option","-t",t,"@bostrat-sid",p)),await Nr(M,N("set-option","-t",t,"@bostrat-agent",s)),await Ja(S_),await Et(t,m,{kind:"swap-launch"}),await G({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:w,forced:i,envRespawned:f}}import rE from"node:fs/promises";function A_(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===`
91
- `){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function Zo(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 Dr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Ct=t=>t&&!/[$`]/.test(t)?t:null,gt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Pr=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},aw=t=>t.startsWith("-"),ow=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(aw(o)){e.has(o)&&r++;continue}n.push(o)}return n}var __={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"},E_={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 iw(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 I_(t){let e=iw(t),n=Ct(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 jl(t){if(t.includes("--files")){let s=Ct(ut(t,ow)[0]);return s&&s!=="."?`List files in ${Pr(s)}`:"List files"}let e=ut(t,ow),n=e[0];if(!n)return null;let r=Ct(e.slice(1).find(s=>s!==".")),o=Dr(n,40);return r?`Search for \u201C${o}\u201D in ${Pr(r)}`:`Search for \u201C${o}\u201D`}function Ll(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 Fl(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 R_(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 sw(t){return t.find(e=>/^https?:\/\//.test(e))||null}var $_={git:t=>{let e=ut(t,new Set(["-C","-c"])),n=__[e[0]];return n?n(e,t):null},gh:t=>{let e=ut(t),n=E_[e[0]];return n?n(e):null},sed:I_,rg:jl,grep:jl,egrep:jl,nl:t=>{let e=Ct(ut(t)[0]);return e?`Read ${gt(e)}`:null},cat:t=>{let e=Ct(ut(t)[0]);return e?`Read ${gt(e)}`:null},head:t=>{let e=Ct(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${gt(e)}`:null},tail:t=>{let e=Ct(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${gt(e)}`:null},wc:t=>{let e=Ct(ut(t)[0]);return e?`Count lines in ${gt(e)}`:"Count input lines"},ls:t=>{let e=Ct(ut(t)[0]);return e&&e!=="."?`List files in ${Pr(e)}`:"List files"},find:t=>Ct(t[1])&&!aw(t[1])&&t[1]!=="."?`Find files in ${Pr(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Ct(ut(t)[0]);return e?`Create directory ${Pr(e)}`:null},touch:t=>{let e=Ct(ut(t)[0]);return e?`Create ${gt(e)}`:null},rm:t=>{let e=Ct(ut(t)[0]);return e?`Delete ${Pr(e)}`:null},mv:t=>{let e=Ct(ut(t)[0]);return e?`Move ${gt(e)}`:null},cp:t=>{let e=Ct(ut(t)[0]);return e?`Copy ${gt(e)}`:null},chmod:t=>{let e=Ct(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=sw(t);return e?`Fetch ${Dr(e,56)}`:"Fetch a URL"},wget:t=>{let e=sw(t);return e?`Fetch ${Dr(e,56)}`:"Fetch a URL"},npm:Ll,pnpm:Ll,yarn:Ll,npx:t=>{let e=ut(t)[0];return e?`Run ${e}`:null},node:t=>Fl(t,"Node"),python:t=>Fl(t,"Python"),python3:t=>Fl(t,"Python"),make:t=>{let e=ut(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:R_,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}},O_=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function M_(t){let e=Qo(Zo(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(O_.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=$_[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(`
92
- `,n);i!==-1&&(e=e.slice(0,i))}let r=A_(e);for(let i=0;i<r.length-1;i++){let c=Qo(Zo(r[i])),l=Qo(Zo(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(gt(c[0]))&&gt(l[0])==="sed"){let u=iw(l),f=ut(c)[0];if(u&&f)return Dr(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=M_(i);c&&o.push({d:c,head:gt(Qo(Zo(i))[0]||""),sub:ut(Qo(Zo(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":Dr(s?s.d:o[0].d,88)}var Wl=new Set(["Bash"]);function Br(t){let e=t.split(`
93
- `).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function gn(t,e=""){let n=e.trim();return n||Ul(t)||Br(t)}function Hl(t,e){if(t){if(Array.isArray(t)){for(let n of t)Hl(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&Wl.has(t.name)){let n=String(t.input?.command||"").trim();n&&e.push({id:t.id||null,tool:t.name,command:n,description:String(t.input?.description||"").trim()})}for(let n of Object.values(t))Hl(n,e)}}}async function cw(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Ft(t,e)}catch{return[]}let o=[];for(let s of r.split(`
94
- `)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(Hl(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:gn(f.command,f.description),ts:u})}return o.slice(-n)}function lw(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,w)=>w>=s&&(m.ts||0)>(l.ts||0)),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}var C_=/^Async (?:agent|task) launched/i,N_=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 uw(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Ft(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
95
- `)){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?Br(p):null),detail:p?Br(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&C_.test(N_(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 ts from"node:fs";import D_ from"node:fs/promises";import Xn from"node:path";import B_ from"node:os";import j_ from"node:crypto";import L_ from"node:zlib";var Xa=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),ae=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,jr=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function yn(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 ql=t=>`"${String(t).replace(/"/g,'\\"')}"`;function P_(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 dw(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:ae(`read ${yn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${jr(r)}`:`Read ${jr(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?yn(e.path,n):null,s=e?.glob?` --glob ${ql(e.glob)}`:"",a=ae(String(r),40);return{command:ae(`grep ${ql(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?yn(e.target_directory,n):null;return{command:ae(`glob ${ql(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${ae(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:ae(`edit ${yn(e.path,n)}`,140),description:`Edit ${jr(e.path)}`}:null;case"Write":return e?.path?{command:ae(`write ${yn(e.path,n)}`,140),description:`Write ${jr(e.path)}`}:null;case"Delete":return e?.path?{command:ae(`delete ${yn(e.path,n)}`,140),description:`Delete ${jr(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>yn(s,n)).filter(Boolean);return{command:ae(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=P_(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:ae(`apply_patch ${yn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${jr(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:ae(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var F_=process.env.BOSTRAT_CURSOR_CHATS||Xn.join(B_.homedir(),".cursor","chats");function mw(t){let e=j_.createHash("md5").update(String(t)).digest("hex");return Xn.join(F_,e)}function hw(t){try{let e=JSON.parse(ts.readFileSync(Xn.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 U_(t){let e=mw(t),n;try{n=ts.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Xn.join(e,o.name),a=Xn.join(s,"store.db");if(!ts.existsSync(a))continue;let i=hw(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 Ya(t,e){if(!t)return null;if(e){let n=Xn.join(mw(t),e),r=Xn.join(n,"store.db");if(ts.existsSync(r))return{id:e,dir:n,store:r,...hw(n)||{createdAtMs:null,updatedAtMs:null}}}return U_(t)}function fw(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function H_(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=L_.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function W_(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 q_(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!Xa.has(r.toolName))continue;let o=dw(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 K_=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(`
96
- `).trim():"",G_=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],pw=process.getBuiltinModule?.("node:sqlite"),Lr={turns:[],trailingCommands:[],model:null,requestIds:[]};async function Kl(t,e){if(!pw)return Lr;let{DatabaseSync:n}=pw,r;try{r=new n(t,{readOnly:!0})}catch{return Lr}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 Lr;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Lr}let a=s.lastUsedModel||null,i=s.latestRootBlobId?fw(r,s.latestRootBlobId):null;if(!i)return{...Lr,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of W_(i)){let m=H_(fw(r,p));if(!m)continue;let w=m.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let S of m.content){let E=S?.providerOptions?.cursor?.modelName;E&&(f=E)}l.push(...q_(m.content,e).map((S,E)=>({...S,id:S.id||`${p}:${E}`}))),u.push(...G_(m.content));let y=K_(m.content);if(!y)continue;let b=[...new Set(u)];u=[],c.push({id:p,text:y,tools:b.length?b:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return Lr}finally{try{r.close()}catch{}}}function ww(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(ts.statSync(t.store).mtimeMs)}catch{n=Date.now()}let r=t.createdAtMs;(!r||r>n)&&(r=n-e*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/e)}}async function gw(t,e,{limit:n=80}={}){let r=Ya(t,e);if(!r)return[];let{turns:o,model:s}=await Kl(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=ww(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 yw(t,e){let n=Ya(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Kl(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 bw(t,e,{limit:n=80}={}){let r=Ya(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await Kl(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=ww(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:gn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:gn(u.command,u.description),ts:i+1});return l}async function kw(t,e){let n=Ya(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await D_.stat(n.store)).mtimeMs)}catch{return null}}import xw from"node:fs";import Vl from"node:fs/promises";import Gl from"node:path";import V_ from"node:os";var z_=process.env.BOSTRAT_CODEX_SESSIONS||Gl.join(process.env.CODEX_HOME||Gl.join(V_.homedir(),".codex"),"sessions");async function Sw(t){try{return await Vl.realpath(t)}catch{return t}}function J_(t=z_){let e=[],n=r=>{let o;try{o=xw.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Gl.join(r,s.name);if(s.isDirectory())n(a);else if(s.isFile()&&s.name.startsWith("rollout-")&&s.name.endsWith(".jsonl")){let i=/-([0-9a-f-]{36})\.jsonl$/i.exec(s.name);try{e.push({id:i?i[1]:null,file:a,mtimeMs:xw.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function X_(t,e=1048576){let n;try{n=await Vl.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(`
97
- `);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function Y_(t){let e=await X_(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 Yn(t,e){if(!t&&!e)return null;let n=J_();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await Sw(t);for(let o of n){let s=await Y_(o.file);if(s&&(s===t||await Sw(s)===r))return o}return null}var Z_=new Set(["output_text","text"]),Q_=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&Z_.has(e.type)).map(e=>e.text).join(`
98
- `).trim():"";async function vw(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Yn(t,e);if(!o)return[];let s;try{s=await Ft(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
99
- `)){let p=d.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let S=m.payload?.type;S==="task_started"?(l=!0,u=!1,f=m.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Jl,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 w=m.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,zl.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let y=Q_(w.content),b=c.length?c.slice():null;c=[],!(!y&&!b)&&a.push({role:"assistant",text:y,tools:b||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 es=new Set(["exec_command","shell","exec"]),zl=new Set(["wait","write_stdin"]),Jl="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 tE(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 eE(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+=`
100
- `;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 nE(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=eE(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Tw(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Yn(t,e);if(!o)return[];let s;try{s=await Ft(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
101
- `)){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"&&es.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=tE(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=nE(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:gn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Aw(t,e){let n=await Yn(t,e);if(!n)return null;try{return Math.round((await Vl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var Fr=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}},Xl=class extends Fr{transcript(e){return!e?.cwd||!e?.sessionId?null:Me(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Bc(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?cw(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?uw(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await rE.stat(n)).mtimeMs)}catch{return null}}},Yl=class extends Fr{async readMessages(e,n){return gw(e.cwd,e.sessionId,n)}async readCommands(e,n){return bw(e.cwd,e.sessionId,n)}async updatedAt(e){return kw(e.cwd,e.sessionId)}},Zl=class extends Fr{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return vw(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return Tw(e.cwd,this.sessionId(e),n)}async updatedAt(e){return Aw(e.cwd,this.sessionId(e))}},oE=new Fr("unknown"),_w=new Map;function Ql(t){return _w.set(t.agent,t),t}function ns(t){return _w.get(t)||oE}Ql(new Xl("claude",Wl));Ql(new Yl("cursor",Xa));Ql(new Zl("codex",es));var tu=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function eu(t,e={}){return(await ns(t.agent).readMessages(t,e)).map(tu(t))}async function Ew(t,e={}){return(await ns(t.agent).readCommands(t,e)).map(tu(t))}async function Iw(t,e={}){return(await ns(t.agent).readJobs(t,e)).map(tu(t))}var Ow=/to navigate\b.*\bEsc to cancel|Enter to select\b/,ou=/Chat about this/i,Zn=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Rw=/^(type something|chat about this|submit)\b/i,Qn=/^[─—-]{6,}\s*$/,su=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function tr(t){let e=t.search(su);return(e>=0?t.slice(0,e):t).trim()}function sE(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function aE(t){let e=String(t).trim();return!!(!e||Qn.test(t)||su.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function iE(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=cE(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function cE(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 $w(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Mw=/^\s*Switch model\?\s*$/m;function lE(t,e=""){let n=String(t);if(!Mw.test(n))return null;let r=n.replace(/\r/g,"").split(`
102
- `),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(Zn);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&&!Qn.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:tr(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 Cw=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,uE=/^\s*[─—▔-]{6,}\s*$/;function dE(t,e=""){let n=String(t).replace(/\r/g,"").split(`
103
- `),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Cw.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(Zn);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&&!(uE.test(n[c])||Qn.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:tr(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 fE(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
104
- `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Zn);l&&o.push({cursor:!!l[1],label:l[4].trim()})}let s=o.filter(c=>/^(submit answers|cancel)$/i.test(c.label));if(s.length<2)return null;let a=s.map(c=>({label:c.label,description:""})),i=Math.max(0,s.findIndex(c=>c.cursor));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}var nu=/Resume from summary/i,pE=/^\s*This session is\b/;function mE(t,e=""){let n=String(t);if(!nu.test(n))return null;let r=n.replace(/\r/g,"").split(`
105
- `),o=-1;for(let u=r.length-1;u>=0;u--)if(pE.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(Zn);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&&!Qn.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>nu.test(u.label)))return null;let i=s.map(u=>({label:tr(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 er=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function au(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(Zn);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 Nw=/Choose the text style that looks best with your terminal/,Pw=/Let's get started\./;function hE(t,e=""){let n=String(t);if(!Nw.test(n)||!Pw.test(n)||er.test(n))return null;let r=n.replace(/\r/g,"").split(`
106
- `),o=au(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:tr(s.label),description:""}))}]}:null}var Dw=/Select login method:/;function wE(t,e=""){let n=String(t);if(!Dw.test(n)||er.test(n))return null;let r=n.replace(/\r/g,"").split(`
107
- `),o=au(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:tr(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 iu=/Paste code here if prompted/,gE=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function yE(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!gE.test(o)||iu.test(t[r]))break;n+=o}return n}function bE(t,e=""){let n=String(t);if(!iu.test(n)||er.test(n))return null;let r=n.replace(/\r/g,"").split(`
108
- `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=yE(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:/,kE=/Press Enter to retry\./;function xE(t,e=""){let n=String(t);if(!ru.test(n)||!kE.test(n)||er.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
109
- `)].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 Bw=/Quick safety check: Is this a project you created or one you trust/;function SE(t,e=""){let n=String(t);if(!Bw.test(n)||er.test(n))return null;let r=n.replace(/\r/g,"").split(`
110
- `),o=au(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:tr(s.label),description:""}))}]}:null}var jw=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function vE(t,e=""){let n=String(t);if(er.test(n))return null;let r=n.replace(/\r/g,"").split(`
111
- `),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(jw);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(Qn.test(r[l])||su.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 TE(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(ou.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 AE(t,e=""){let n=String(t);if(!Ow.test(n)||!ou.test(n))return null;let r=n.replace(/\r/g,"").split(`
112
- `),o=TE(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let v=i;v<c;v++){let D=r[v].match(Zn);D&&(u<0&&(u=v),l.push({line:v,cursor:!!D[1],checked:D[3]!=null&&D[3]!==" ",hasCheckbox:D[3]!=null,label:D[4].trim(),desc:""}))}if(!l.length)return null;for(let v of l)for(let D=v.line+1;D<Math.min(r.length,v.line+4)&&!(Zn.test(r[D])||Qn.test(r[D]));D++){let B=r[D].trim();if(!(!B||aE(r[D])||Rw.test(B))){v.desc=sE(B);break}}let f=l.filter(v=>!Rw.test(v.label));if(!f.length)return null;let d=f.some(v=>v.hasCheckbox),p=Math.max(0,f.findIndex(v=>v.cursor)),m=s>=0?s+1:0,w=[];for(let v=m;v<u;v++){let D=r[v].trim();D&&!Qn.test(r[v])&&!/[☐☒]/.test(r[v])&&w.push(D)}let y=w.join(" ")||(s>=0?$w(r[s]):"")||"The agent is asking a question",b=s>=0?iE(r[s],y):null,S=b?.currentTabLabel||(s>=0?$w(r[s]):""),E=f.map(v=>({label:tr(v.label),description:v.desc})),O=E.map(v=>v.label).join("|"),R=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${S}:${y}:${O}`.slice(0,240),C=b?`claude:ask:${b.tabs.map(v=>v.label).join("|")}`.slice(0,240):R;return{provider:"claude",box:e,multiSelect:d,cursorIndex:p,promptId:R,groupId:C,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:y,header:S,multiSelect:d,options:E}]}}function Ur(t,e){let n=String(e||"");return n?t==="claude"?Ow.test(n)&&ou.test(n)||Mw.test(n)||nu.test(n)||Cw.test(n.trimEnd().split(`
113
- `).pop()||"")?!0:er.test(n)?!1:Nw.test(n)&&Pw.test(n)||Dw.test(n)||iu.test(n)||ru.test(n)||Bw.test(n)||jw.test(n.trimEnd().split(`
114
- `).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 nr(t,e){return!t||!e?null:t.agent==="claude"?fE(e,t.name)||AE(e,t.name)||lE(e,t.name)||mE(e,t.name)||xE(e,t.name)||bE(e,t.name)||wE(e,t.name)||hE(e,t.name)||SE(e,t.name)||vE(e,t.name)||dE(e,t.name):t.agent==="cursor"||t.agent==="codex"?Ep(t.agent,e,t.name):null}async function cu(t){if(!t)return null;let e=await an(t.name).catch(()=>"");return nr(t,e)}function Lw(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 Fw(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 Hr(t){return typeof t=="string"?t.trim():""}var Uw=/^\s*─{8,}\s*$/,_E=/^❯[  ]?(.*)$/;function rs(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
115
- `).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Uw.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Uw.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(_E);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 St(t){return String(t||"").replace(/\s+/g," ").trim()}function Hw(t,e,{slackMs:n=2e4}={}){let r=St(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&&St(o.text).includes(r))}function Ww(t,e,n,{busy:r=!1}={}){let o=rs(t,n);return o&&lu(e,o)?"stuck":r||o?"submitted":"unknown"}function lu(t,e){let n=St(t),r=St(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import EE from"node:crypto";var uu=new Set(["cursor","codex"]),Kw=4,IE=4,RE=2,$E=24,qw=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,os=t=>typeof t=="string"?t.trim():"";function OE(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,Kw)){let o=os(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,IE)){let c=os(i?.label);if(!c)return null;let l=os(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<RE)return null;n.push({question:o,header:os(r?.header).slice(0,$E),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function du(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;qw.lastIndex=0;let e=null;for(let o;o=qw.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=OE(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function fu(t,e,n){let r=EE.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function pu(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=Kw}={}){if(!Array.isArray(t)||!e||!uu.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||du(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=fu(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 Gw(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=os(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
85
+ }`,{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 e_(t,e){let n=await ke(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 n_(t,e,n){if(!(await ke(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 Dr(t){return((await ke(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var kh=255;async function vl(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>kh,a=s?`${o.slice(0,kh-1).trimEnd()}\u2026`:o,i=await ke(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 Sl(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await ke(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 Eh={linear:{api:Tl,config:Vn},jira:{api:kl,config:Ua}};async function Ol(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(Eh)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function Xn(t){let e=Eh[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function Ml(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 Ih=()=>process.env.BOSTRAT_STATE_DIR||Ah.join(process.env.HOME||r_.tmpdir(),".bostrat"),Il=()=>Ah.join(Ih(),"tickets.json"),Rh=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Wa=t=>`${Rh()}/track/${t}`,hn=null,Al=null,_l=Promise.resolve();async function Xo(){return hn||(Al??=zo.readFile(Il(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(hn=t,!e&&ht()){let{records:n,source:r}=await Rt("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;hn[s]=i}Object.keys(hn).length&&await Rl().catch(()=>{})}}return hn}).finally(()=>Al=null),Al)}async function Rl(){let t=JSON.stringify({tickets:hn},null,2),e=`${Il()}.${process.pid}.tmp`;await zo.mkdir(Ih(),{recursive:!0}),await zo.writeFile(e,t),await zo.rename(e,Il())}async function wn(t=null){if(_l=_l.then(Rl,Rl),await _l.catch(()=>{}),!t)return;let e=hn?.[t];e?Nt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):ne("tickets",t).catch(()=>{})}async function xe(t){return(await Xo())[t]??null}async function qa(){return{...await Xo()}}async function $h(){return(await Ol()).length>0}async function Br(t,e,{source:n="api"}={}){let r=await Ol();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 Xo())[t.id]=c,await wn(t.id),await R({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Jo(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 Vo(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Ch(t.id,c,"linked"),c}async function Cl(t){let e=await Xo();return e[t]?(delete e[t],await wn(t),!0):!1}var $l=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Oh(t,e){if(!t.track?.cwd)return null;let n=me(await L({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function o_(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${$l(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${$l(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function s_(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($l(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Jo(t,e){let n=await Xn(e);if(!n)return;let r=await Oh(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:Wa(t),title:`Bostrat Track: ${e.track.name}`,subtitle:s_(e,r)})}function Ka(t){let e=new URL(Rh());return`${e.protocol}//${t}.${e.host}/`}var a_={linked:"linked",opened:"prOpened",merged:"prMerged"};function i_(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 Mh(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await wn(t),await R({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Ch(t,e,n){let r=await Xn(e);if(!r)return;let o=r.cfg.ticketFlow?.[a_[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=Ml(s,a),c=i_(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 Mh(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await R({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:_h(),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 Nh(t){let e=await xe(t);if(!e)throw new Error("no ticket linked to this Track");let n=await Xn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=Ml(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await wn(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 Nl(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await xe(t);if(!r)throw new Error("no ticket linked to this Track");let o=await Xn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Ml(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 wn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Mh(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Ph(t,e){let n=await xe(t);if(!n)throw new Error("no ticket linked to this Track");let r=await Xn(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await R({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function Dh(t,e,n){if(!e)throw new Error("offerId required");let r=await L({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 Nl(t.id,o.payload.to?.id,{mode:"offer"})),await R({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var xh=50;async function Bh(){let t=await Vn();if(!t)throw new Error("no ticketing integration configured");return Dr(t)}function c_(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>xh)throw new Error(`too many issues (${n.length}); cap is ${xh} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function l_(t,e,n,r){let o=r??await Dr(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 xe(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 jh(t,e){let n=await Vn();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=c_(e),o=await Dr(n),s=await l_(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=_h();return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function Lh(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await L({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await Vn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Dr(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let m=await vl(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 w=await Sl(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}await R({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await xe(t.id)&&(p=await Br(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function Vo(t,e){let n=await Xn(t);n&&await n.api.createComment(n.cfg,t,e)}async function u_(t,e,n){let r=await Xn(t);if(!r)return null;let o=await pn(e);if(!o)return null;let s=Ne(o,n);if(!s)return null;let a=await zo.readFile(zt(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 vh=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),El=new Map;function d_(t,e){if(vh()<=0)return Jo(t,e);if(El.has(t))return;let n=setTimeout(()=>{El.delete(t),xe(t).then(r=>r?Jo(t,r):null).catch(()=>{})},vh());n.unref?.(),El.set(t,n)}var Sh=new Map;async function Th(t,e,n){let r=Sh.get(t)??0;Date.now()-r<600*1e3||(Sh.set(t,Date.now()),await R({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function f_(t,{resolveTrack:e=null}={}){let n=await xe(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await Ol())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 Br(s,o,{source:"chat"})}catch(a){await Th(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 wn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await wn(t.trackId),await Jo(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 wn(t.trackId),await Jo(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Oh(n,t.trackId).catch(()=>null):null;await Vo(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
86
+
87
+ Agent cost for this Track: ${o_(o)}.`:"")),await Ch(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await u_(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Vo(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
88
+
89
+ ![${r.name}](${o})`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Vo(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 Vo(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Ka(r.slug)}
90
+ _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await d_(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Th(t.trackId,s?.cwd??null,o)}}function Fh({resolveTrack:t}){Ft.on("event",e=>{f_(e,{resolveTrack:t}).catch(()=>{})})}async function Uh(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Rt("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Xo(),r=0;for(let[o,s]of Object.entries(n))await Nt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import p_ from"node:crypto";import Ga from"node:fs/promises";import Hh from"node:path";import m_ from"node:os";var Wh=process.env.BOSTRAT_STATE_DIR||Hh.join(process.env.HOME||m_.tmpdir(),".bostrat"),Bl=Hh.join(Wh,"pr-links.json"),K=(t,e)=>`${t}#${Number(e)}`,qh=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=p_.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},gn=null,Pl=null,Dl=Promise.resolve();async function kt(){return gn||(Pl??=Ga.readFile(Bl,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(gn=t,!e&&ht()){let{records:n,source:r}=await Rt("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;gn[K(i.repo,i.number)]=i}Object.keys(gn).length&&await jl().catch(()=>{})}}return gn}).finally(()=>Pl=null),Pl)}async function jl(){let t=JSON.stringify({links:gn},null,2),e=`${Bl}.${process.pid}.tmp`;await Ga.mkdir(Wh,{recursive:!0}),await Ga.writeFile(e,t),await Ga.rename(e,Bl)}async function Et(t){Dl=Dl.then(jl,jl),await Dl.catch(()=>{});let e=gn?.[t];e&&Nt("prlinks",{id:qh(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var h_=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 kt(),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:h_.has(a),firstSeen:Date.now()},await Et(K(t,e)),{link:i[c],created:!0})}async function Yo(t,e){if(!t||e==null)return null;let n=(await kt())[K(t,e)];return!n||n.announced?null:(n.announced=!0,await Et(K(t,e)),n)}async function Pt(t,e){return!t||e==null?null:(await kt())[K(t,e)]??null}async function yn(t,e,n){if(!t||e==null)return null;let r=(await kt())[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 Et(K(t,e)),r):null}async function Kh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await kt())[K(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Et(K(t,e)),o):null}async function Gh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await kt())[K(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Et(K(t,e)),o):null}async function Va(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await kt())[K(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Et(K(t,e)),r)}async function za(t,e){if(!t||e==null)return null;let n=(await kt())[K(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Et(K(t,e)),n)}async function Ja(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await kt())[K(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Et(K(t,e)),r)}async function Xa(t,e){if(!t||e==null)return null;let n=(await kt())[K(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Et(K(t,e)),n)}async function Vh(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await kt())[K(t,e)];return r?(r.baseFoldAt=n,await Et(K(t,e)),r):null}async function zh(t,e){if(!t||e==null)return null;let n=(await kt())[K(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Et(K(t,e)),n)}async function Jh(t,e,n){if(!t||e==null||!n)return null;let r=(await kt())[K(t,e)];return r?(r.lastHeadSha=n,await Et(K(t,e)),r):null}async function Zo(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await kt())[K(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Et(K(t,e)),r):null}async function Yn(t,e,n=!0){if(!t||e==null)return null;let r=(await kt())[K(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Et(K(t,e)),r):null}async function Qo(t,e,n=!0){if(!t||e==null)return null;let r=(await kt())[K(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Et(K(t,e)),r):null}async function Xh(t,e,n){if(!t||e==null)return null;let r=(await kt())[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 Et(K(t,e)),r}async function ts(t,e){if(!t||e==null)return null;let n=(await kt())[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 Et(K(t,e)),n)}var w_=new Set(["merged","closed","lost"]);async function Yh(){return Object.values(await kt()).filter(t=>!w_.has(t.state))}async function ve(){return Object.values(await kt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function Zh(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Rt("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await kt(),r=0;for(let[o,s]of Object.entries(n))await Nt("prlinks",{id:qh(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var tw=t=>Math.round(t*100)/100;function g_(t){return!t||t.kind==="setup"?[]:Wt(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():Qh.basename(e.base)}))}function ew(t){let e=new Map;for(let n of t||[])for(let r of g_(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function Ya(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 Ll=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function y_(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 Fl(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(Ll(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Ll(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(`${Ll(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=me(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?y_(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function b_(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 nw(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=Ya(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:tw(l),firstActivityAt:c,lastActivityAt:i}}async function rw(t){let e=ew(t),n=await ve().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 L(i).catch(()=>[]);r.set(i.id,me(c).totalUsd)}let s=[...e.values()].map(a=>nw(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function ow(t,e){let r=ew(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([ve().catch(()=>[]),qn().catch(()=>[]),Pa().catch(()=>[]),ja().catch(()=>[]),qa().catch(()=>({}))]),l=new Map,u=[],f=(d,p)=>(Ya(p)||p.createdAt||0)-(Ya(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let p=await L(d).catch(()=>[]),m=me(p);l.set(d.id,m.totalUsd);let w=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:b_(d),handoffs:(d.events||[]).filter(y=>y.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:Wt(d.workspace).map(y=>y.repo||Qh.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:w?{provider:w.provider,key:w.key,url:w.url??null}:null,usd:tw(m.totalUsd),lastActivityAt:Ya(d)})}return{...nw(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as k_}from"node:child_process";import{promisify as x_}from"node:util";import{randomUUID as v_}from"node:crypto";var jr=x_(k_),Za=t=>new Promise(e=>setTimeout(e,t)),S_=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,sw=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,T_=250,A_=400,aw={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}};async function Ul(t,e){let n=Date.now()+e;for(;;){if(await Sr(t))return!0;if(Date.now()>n)return!1;await Za(T_)}}async function __(t,e){let n=aw[e]||aw.default;n.escape&&(await St(t,["Escape"],{kind:"swap-exit"}),await Za(150)),n.wipe&&await pa(t,400,{kind:"swap-exit"}),await ma(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Me(e).submitDelayMs||0;await Za(Math.max(r,250)),await St(t,["Enter"],{kind:"swap-exit"})}async function E_(t,e){try{let{stdout:n}=await jr(M,N("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function iw(t){await jr(M,N("respawn-pane","-k","-t",t))}async function Hl(t,e={}){if(!yt(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||sn(n),a=Number(e.exitTimeoutMs)||S_;await q({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Sr(t)&&(await __(t,o),!await Ul(t,a)&&(i=!0,await q({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await iw(t),!await Ul(t,sw))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&Ln(n),l=c?Xs():null,u=await E_(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await jr(M,N("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await jr(M,N("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?ca(y,l):y}if(f&&!i&&(await iw(t),!await Ul(t,sw)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||v_(),m=d?n:Nc(n,p),w=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await en(r.cwd),s==="codex"&&await mr(r.cwd),s==="cursor"&&await hr(r.cwd),await Un(t),await jr(M,N("set-option","-t",t,"@bostrat-sid",p)),await jr(M,N("set-option","-t",t,"@bostrat-agent",s)),await Za(A_),await _t(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:w,forced:i,envRespawned:f}}import aE from"node:fs/promises";function I_(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===`
91
+ `){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function es(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 ns(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 Fr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Ot=t=>t&&!/[$`]/.test(t)?t:null,wt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Lr=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},uw=t=>t.startsWith("-"),cw=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(uw(o)){e.has(o)&&r++;continue}n.push(o)}return n}var R_={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 ${wt(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"},$_={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 dw(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 O_(t){let e=dw(t),n=Ot(ut(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${wt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${wt(n)}`:t.includes("-i")&&n?`Edit ${wt(n)}`:null}function Wl(t){if(t.includes("--files")){let s=Ot(ut(t,cw)[0]);return s&&s!=="."?`List files in ${Lr(s)}`:"List files"}let e=ut(t,cw),n=e[0];if(!n)return null;let r=Ot(e.slice(1).find(s=>s!==".")),o=Fr(n,40);return r?`Search for \u201C${o}\u201D in ${Lr(r)}`:`Search for \u201C${o}\u201D`}function ql(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 Kl(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 ${wt(n)}`:null}function M_(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 lw(t){return t.find(e=>/^https?:\/\//.test(e))||null}var C_={git:t=>{let e=ut(t,new Set(["-C","-c"])),n=R_[e[0]];return n?n(e,t):null},gh:t=>{let e=ut(t),n=$_[e[0]];return n?n(e):null},sed:O_,rg:Wl,grep:Wl,egrep:Wl,nl:t=>{let e=Ot(ut(t)[0]);return e?`Read ${wt(e)}`:null},cat:t=>{let e=Ot(ut(t)[0]);return e?`Read ${wt(e)}`:null},head:t=>{let e=Ot(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${wt(e)}`:null},tail:t=>{let e=Ot(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${wt(e)}`:null},wc:t=>{let e=Ot(ut(t)[0]);return e?`Count lines in ${wt(e)}`:"Count input lines"},ls:t=>{let e=Ot(ut(t)[0]);return e&&e!=="."?`List files in ${Lr(e)}`:"List files"},find:t=>Ot(t[1])&&!uw(t[1])&&t[1]!=="."?`Find files in ${Lr(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Ot(ut(t)[0]);return e?`Create directory ${Lr(e)}`:null},touch:t=>{let e=Ot(ut(t)[0]);return e?`Create ${wt(e)}`:null},rm:t=>{let e=Ot(ut(t)[0]);return e?`Delete ${Lr(e)}`:null},mv:t=>{let e=Ot(ut(t)[0]);return e?`Move ${wt(e)}`:null},cp:t=>{let e=Ot(ut(t)[0]);return e?`Copy ${wt(e)}`:null},chmod:t=>{let e=Ot(ut(t)[1]);return e?`Change permissions on ${wt(e)}`:null},diff:t=>{let e=ut(t);return e.length>=2?`Compare ${wt(e[0])} and ${wt(e[1])}`:null},curl:t=>{let e=lw(t);return e?`Fetch ${Fr(e,56)}`:"Fetch a URL"},wget:t=>{let e=lw(t);return e?`Fetch ${Fr(e,56)}`:"Fetch a URL"},npm:ql,pnpm:ql,yarn:ql,npx:t=>{let e=ut(t)[0];return e?`Run ${e}`:null},node:t=>Kl(t,"Node"),python:t=>Kl(t,"Python"),python3:t=>Kl(t,"Python"),make:t=>{let e=ut(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:M_,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}},N_=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function P_(t){let e=ns(es(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(wt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return Gl(e[2]);let n=wt(e[0]);if(N_.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=C_[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function Gl(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
92
+ `,n);i!==-1&&(e=e.slice(0,i))}let r=I_(e);for(let i=0;i<r.length-1;i++){let c=ns(es(r[i])),l=ns(es(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(wt(c[0]))&&wt(l[0])==="sed"){let u=dw(l),f=ut(c)[0];if(u&&f)return Fr(u.from===u.to?`Read line ${u.from} of ${wt(f)}`:`Read lines ${u.from}\u2013${u.to} of ${wt(f)}`,88)}}let o=[];for(let i of r){let c=P_(i);c&&o.push({d:c,head:wt(ns(es(i))[0]||""),sub:ut(ns(es(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":Fr(s?s.d:o[0].d,88)}var zl=new Set(["Bash"]);function Ur(t){let e=t.split(`
93
+ `).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function bn(t,e=""){let n=e.trim();return n||Gl(t)||Ur(t)}function Vl(t,e){if(t){if(Array.isArray(t)){for(let n of t)Vl(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&zl.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))Vl(n,e)}}}async function fw(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Lt(t,e)}catch{return[]}let o=[];for(let s of r.split(`
94
+ `)){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(Vl(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:bn(f.command,f.description),ts:u})}return o.slice(-n)}function pw(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,w)=>w>=s&&(m.ts||0)>(l.ts||0)),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}var D_=/^Async (?:agent|task) launched/i,B_=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 mw(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Lt(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
95
+ `)){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?Ur(p):null),detail:p?Ur(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&D_.test(B_(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 rs from"node:fs";import L_ from"node:fs/promises";import Zn from"node:path";import F_ from"node:os";import U_ from"node:crypto";import H_ from"node:zlib";var Qa=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),se=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Hr=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function kn(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 Jl=t=>`"${String(t).replace(/"/g,'\\"')}"`;function j_(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 hw(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:se(`read ${kn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Hr(r)}`:`Read ${Hr(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?kn(e.path,n):null,s=e?.glob?` --glob ${Jl(e.glob)}`:"",a=se(String(r),40);return{command:se(`grep ${Jl(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?kn(e.target_directory,n):null;return{command:se(`glob ${Jl(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${se(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:se(`edit ${kn(e.path,n)}`,140),description:`Edit ${Hr(e.path)}`}:null;case"Write":return e?.path?{command:se(`write ${kn(e.path,n)}`,140),description:`Write ${Hr(e.path)}`}:null;case"Delete":return e?.path?{command:se(`delete ${kn(e.path,n)}`,140),description:`Delete ${Hr(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>kn(s,n)).filter(Boolean);return{command:se(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=j_(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:se(`apply_patch ${kn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Hr(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:se(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var W_=process.env.BOSTRAT_CURSOR_CHATS||Zn.join(F_.homedir(),".cursor","chats");function yw(t){let e=U_.createHash("md5").update(String(t)).digest("hex");return Zn.join(W_,e)}function bw(t){try{let e=JSON.parse(rs.readFileSync(Zn.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 q_(t){let e=yw(t),n;try{n=rs.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Zn.join(e,o.name),a=Zn.join(s,"store.db");if(!rs.existsSync(a))continue;let i=bw(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 ti(t,e){if(!t)return null;if(e){let n=Zn.join(yw(t),e),r=Zn.join(n,"store.db");if(rs.existsSync(r))return{id:e,dir:n,store:r,...bw(n)||{createdAtMs:null,updatedAtMs:null}}}return q_(t)}function ww(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function K_(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=H_.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function G_(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 V_(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!Qa.has(r.toolName))continue;let o=hw(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 z_=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(`
96
+ `).trim():"",J_=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],gw=process.getBuiltinModule?.("node:sqlite"),Wr={turns:[],trailingCommands:[],model:null,requestIds:[]};async function Xl(t,e){if(!gw)return Wr;let{DatabaseSync:n}=gw,r;try{r=new n(t,{readOnly:!0})}catch{return Wr}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 Wr;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Wr}let a=s.lastUsedModel||null,i=s.latestRootBlobId?ww(r,s.latestRootBlobId):null;if(!i)return{...Wr,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of G_(i)){let m=K_(ww(r,p));if(!m)continue;let w=m.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let v of m.content){let E=v?.providerOptions?.cursor?.modelName;E&&(f=E)}l.push(...V_(m.content,e).map((v,E)=>({...v,id:v.id||`${p}:${E}`}))),u.push(...J_(m.content));let y=z_(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 Wr}finally{try{r.close()}catch{}}}function kw(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(rs.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 xw(t,e,{limit:n=80}={}){let r=ti(t,e);if(!r)return[];let{turns:o,model:s}=await Xl(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=kw(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 vw(t,e){let n=ti(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Xl(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 Sw(t,e,{limit:n=80}={}){let r=ti(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await Xl(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=kw(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:bn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:bn(u.command,u.description),ts:i+1});return l}async function Tw(t,e){let n=ti(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await L_.stat(n.store)).mtimeMs)}catch{return null}}import Aw from"node:fs";import Zl from"node:fs/promises";import Yl from"node:path";import X_ from"node:os";var Y_=process.env.BOSTRAT_CODEX_SESSIONS||Yl.join(process.env.CODEX_HOME||Yl.join(X_.homedir(),".codex"),"sessions");async function _w(t){try{return await Zl.realpath(t)}catch{return t}}function Z_(t=Y_){let e=[],n=r=>{let o;try{o=Aw.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Yl.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:Aw.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function Q_(t,e=1048576){let n;try{n=await Zl.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(`
97
+ `);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function tE(t){let e=await Q_(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 Qn(t,e){if(!t&&!e)return null;let n=Z_();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await _w(t);for(let o of n){let s=await tE(o.file);if(s&&(s===t||await _w(s)===r))return o}return null}var eE=new Set(["output_text","text"]),nE=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&eE.has(e.type)).map(e=>e.text).join(`
98
+ `).trim():"";async function Ew(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Qn(t,e);if(!o)return[];let s;try{s=await Lt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
99
+ `)){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:tu,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 w=m.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,Ql.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let y=nE(w.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 os=new Set(["exec_command","shell","exec"]),Ql=new Set(["wait","write_stdin"]),tu="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 rE(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 oE(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+=`
100
+ `;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 sE(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=oE(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Iw(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Qn(t,e);if(!o)return[];let s;try{s=await Lt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
101
+ `)){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"&&os.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=rE(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=sE(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:bn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Rw(t,e){let n=await Qn(t,e);if(!n)return null;try{return Math.round((await Zl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var qr=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}},eu=class extends qr{transcript(e){return!e?.cwd||!e?.sessionId?null:Oe(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Hc(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?fw(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?mw(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await aE.stat(n)).mtimeMs)}catch{return null}}},nu=class extends qr{async readMessages(e,n){return xw(e.cwd,e.sessionId,n)}async readCommands(e,n){return Sw(e.cwd,e.sessionId,n)}async updatedAt(e){return Tw(e.cwd,e.sessionId)}},ru=class extends qr{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return Ew(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return Iw(e.cwd,this.sessionId(e),n)}async updatedAt(e){return Rw(e.cwd,this.sessionId(e))}},iE=new qr("unknown"),$w=new Map;function ou(t){return $w.set(t.agent,t),t}function ss(t){return $w.get(t)||iE}ou(new eu("claude",zl));ou(new nu("cursor",Qa));ou(new ru("codex",os));var su=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function au(t,e={}){return(await ss(t.agent).readMessages(t,e)).map(su(t))}async function Ow(t,e={}){return(await ss(t.agent).readCommands(t,e)).map(su(t))}async function Mw(t,e={}){return(await ss(t.agent).readJobs(t,e)).map(su(t))}var Pw=/to navigate\b.*\bEsc to cancel|Enter to select\b/,lu=/Chat about this/i,tr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Cw=/^(type something|chat about this|submit)\b/i,er=/^[─—-]{6,}\s*$/,uu=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function nr(t){let e=t.search(uu);return(e>=0?t.slice(0,e):t).trim()}function cE(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function lE(t){let e=String(t).trim();return!!(!e||er.test(t)||uu.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function uE(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=dE(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function dE(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 Nw(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Dw=/^\s*Switch model\?\s*$/m;function fE(t,e=""){let n=String(t);if(!Dw.test(n))return null;let r=n.replace(/\r/g,"").split(`
102
+ `),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(tr);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&&!er.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:nr(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 Bw=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,pE=/^\s*[─—▔-]{6,}\s*$/;function mE(t,e=""){let n=String(t).replace(/\r/g,"").split(`
103
+ `),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Bw.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(tr);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&&!(pE.test(n[c])||er.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:nr(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 hE(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
104
+ `),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(tr);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,wE=/^\s*This session is\b/;function gE(t,e=""){let n=String(t);if(!iu.test(n))return null;let r=n.replace(/\r/g,"").split(`
105
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if(wE.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(tr);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&&!er.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:nr(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 rr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function du(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(tr);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 jw=/Choose the text style that looks best with your terminal/,Lw=/Let's get started\./;function yE(t,e=""){let n=String(t);if(!jw.test(n)||!Lw.test(n)||rr.test(n))return null;let r=n.replace(/\r/g,"").split(`
106
+ `),o=du(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:nr(s.label),description:""}))}]}:null}var Fw=/Select login method:/;function bE(t,e=""){let n=String(t);if(!Fw.test(n)||rr.test(n))return null;let r=n.replace(/\r/g,"").split(`
107
+ `),o=du(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:nr(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 fu=/Paste code here if prompted/,kE=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function xE(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!kE.test(o)||fu.test(t[r]))break;n+=o}return n}function vE(t,e=""){let n=String(t);if(!fu.test(n)||rr.test(n))return null;let r=n.replace(/\r/g,"").split(`
108
+ `),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=xE(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 cu=/OAuth error:/,SE=/Press Enter to retry\./;function TE(t,e=""){let n=String(t);if(!cu.test(n)||!SE.test(n)||rr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
109
+ `)].reverse().find(a=>cu.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 Uw=/Quick safety check: Is this a project you created or one you trust/;function AE(t,e=""){let n=String(t);if(!Uw.test(n)||rr.test(n))return null;let r=n.replace(/\r/g,"").split(`
110
+ `),o=du(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:nr(s.label),description:""}))}]}:null}var Hw=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function _E(t,e=""){let n=String(t);if(rr.test(n))return null;let r=n.replace(/\r/g,"").split(`
111
+ `),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(Hw);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(er.test(r[l])||uu.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 EE(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(lu.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 IE(t,e=""){let n=String(t);if(!Pw.test(n)||!lu.test(n))return null;let r=n.replace(/\r/g,"").split(`
112
+ `),o=EE(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 C=r[S].match(tr);C&&(u<0&&(u=S),l.push({line:S,cursor:!!C[1],checked:C[3]!=null&&C[3]!==" ",hasCheckbox:C[3]!=null,label:C[4].trim(),desc:""}))}if(!l.length)return null;for(let S of l)for(let C=S.line+1;C<Math.min(r.length,S.line+4)&&!(tr.test(r[C])||er.test(r[C]));C++){let D=r[C].trim();if(!(!D||lE(r[C])||Cw.test(D))){S.desc=cE(D);break}}let f=l.filter(S=>!Cw.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,w=[];for(let S=m;S<u;S++){let C=r[S].trim();C&&!er.test(r[S])&&!/[☐☒]/.test(r[S])&&w.push(C)}let y=w.join(" ")||(s>=0?Nw(r[s]):"")||"The agent is asking a question",k=s>=0?uE(r[s],y):null,v=k?.currentTabLabel||(s>=0?Nw(r[s]):""),E=f.map(S=>({label:nr(S.label),description:S.desc})),$=E.map(S=>S.label).join("|"),O=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${v}:${y}:${$}`.slice(0,240),P=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:P,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:y,header:v,multiSelect:d,options:E}]}}function Kr(t,e){let n=String(e||"");return n?t==="claude"?Pw.test(n)&&lu.test(n)||Dw.test(n)||iu.test(n)||Bw.test(n.trimEnd().split(`
113
+ `).pop()||"")?!0:rr.test(n)?!1:jw.test(n)&&Lw.test(n)||Fw.test(n)||fu.test(n)||cu.test(n)||Uw.test(n)||Hw.test(n.trimEnd().split(`
114
+ `).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 or(t,e){return!t||!e?null:t.agent==="claude"?hE(e,t.name)||IE(e,t.name)||fE(e,t.name)||gE(e,t.name)||TE(e,t.name)||vE(e,t.name)||bE(e,t.name)||yE(e,t.name)||AE(e,t.name)||_E(e,t.name)||mE(e,t.name):t.agent==="cursor"||t.agent==="codex"?$p(t.agent,e,t.name):null}async function pu(t){if(!t)return null;let e=await cn(t.name).catch(()=>"");return or(t,e)}function Ww(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 qw(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 Gr(t){return typeof t=="string"?t.trim():""}var Kw=/^\s*─{8,}\s*$/,RE=/^❯[  ]?(.*)$/;function as(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
115
+ `).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Kw.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Kw.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(RE);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 xt(t){return String(t||"").replace(/\s+/g," ").trim()}function Gw(t,e,{slackMs:n=2e4}={}){let r=xt(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&&xt(o.text).includes(r))}function Vw(t,e,n,{busy:r=!1}={}){let o=as(t,n);return o&&mu(e,o)?"stuck":r||o?"submitted":"unknown"}function mu(t,e){let n=xt(t),r=xt(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import $E from"node:crypto";var hu=new Set(["cursor","codex"]),Jw=4,OE=4,ME=2,CE=24,zw=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,is=t=>typeof t=="string"?t.trim():"";function NE(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,Jw)){let o=is(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,OE)){let c=is(i?.label);if(!c)return null;let l=is(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<ME)return null;n.push({question:o,header:is(r?.header).slice(0,CE),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function wu(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;zw.lastIndex=0;let e=null;for(let o;o=zw.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=NE(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function gu(t,e,n){let r=$E.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function yu(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=Jw}={}){if(!Array.isArray(t)||!e||!hu.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||wu(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=gu(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 Xw(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=is(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
116
116
  ${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
117
117
  `)}`:s=o,r.length&&o&&(s+=`
118
- ${o}`),s||""}var Vw=`
118
+ ${o}`),s||""}var Yw=`
119
119
  When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
120
120
  requirement, an irreversible choice, several valid approaches \u2014 do not bury the
121
121
  question in prose. End your message with exactly ONE fenced code block tagged
@@ -135,42 +135,42 @@ your turn. The operator sees the questions as an interactive card in their chat
135
135
  answer. Never use the block for rhetorical questions, progress updates, or
136
136
  anything you can decide yourself; keep asking in prose for questions without
137
137
  enumerable options.
138
- `;import{execFile as ME}from"node:child_process";import{promisify as CE}from"node:util";var hu=CE(ME),zw=64*1024*1024;async function yt(t,e,{input:n}={}){let r=hu("git",["-C",t,...e],{maxBuffer:zw});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Za(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 Jw(t){let{stdout:e}=await yt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function NE(t){try{return await yt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function ss(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)=>hu("git",["-C",t,...a],{maxBuffer:zw,env:o,...i});try{let a=await NE(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 hu("rm",["-f",r])}catch{}}}async function wu(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 gu(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await yt(t,["status","--porcelain"]),r=n.split(`
139
- `).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 yu(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 Wr(t,e,n,r){return await yt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Xw(t,e){try{return await yt(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function Yw(t,e,n){return await yt(t,["worktree","add",e,n]),e}async function Qa(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 Zw(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 ee(t,e){try{await yt(t,["worktree","remove","--force",e])}catch{try{await yt(t,["worktree","prune"])}catch{}}}function PE(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 bu(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function ku(t,e,n,r){let{stdout:o}=await yt(t,bu(["diff","--shortstat",e,n],r));return PE(o)}async function xu(t,e,n,r){let{stdout:o}=await yt(t,bu(["diff","--name-only",e,n],r));return o.split(`
140
- `).map(s=>s.trim()).filter(Boolean)}async function Su(t,e,n,r){let{stdout:o}=await yt(t,bu(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await mu(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await mu(t,o,["--3way","--whitespace=nowarn"]),a=await DE(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await mu(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function mu(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 DE(t){try{let{stdout:e}=await yt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
141
- `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import vu from"node:fs/promises";import tg from"node:path";import BE from"node:os";var eg=process.env.BOSTRAT_STATE_DIR||tg.join(process.env.HOME||BE.tmpdir(),".bostrat"),ng=()=>tg.join(eg,"usage-caps.json"),rg=.8;function og(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(e.getDate()-(e.getDay()+6)%7),e.getTime()}function sg(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(1),e.getTime()}function ag(t){let e=new Date(og(t));return e.setDate(e.getDate()+7),e.getTime()}function ig(t){let e=new Date(sg(t));return e.setMonth(e.getMonth()+1),e.getTime()}var qr={weekly:{startOf:og,nextStart:ag,capKey:"weeklyUsd"},monthly:{startOf:sg,nextStart:ig,capKey:"monthlyUsd"}},ie=null,Au=!1,qe={weekly:0,monthly:0},rr=new Map,Kr=new Set,Tu=null,jE=t=>{let e=rr.get(t);return e||(e={weekly:0,monthly:0},rr.set(t,e)),e};function ei(t=Date.now()){for(let[e,n]of Object.entries(qr)){let r=n.startOf(t);if(r!==qe[e]){qe[e]=r;for(let o of rr.values())o[e]=0;for(let o of Kr)o.includes(`:${e}:`)&&!o.endsWith(`:${r}`)&&Kr.delete(o)}}}function cg(t,e){let n=ie?.caps?.[t]?.[qr[e].capKey];return Number.isFinite(n)&&n>0?n:null}function as(t,e=Date.now()){if(!t||!Au||!ie?.caps)return null;ei(e);let n=rr.get(t);for(let r of Object.keys(qr)){let o=cg(t,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:t,window:r,limitUsd:o,spentUsd:ti(s),resetsAt:qr[r].nextStart(e)}}return null}function lg(t,e){let n=`hit:${t}:${e.provider}:${e.window}:${qe[e.window]}`;return Kr.has(n)?!1:(Kr.add(n),!0)}function Qw(t,e,n,r=Date.now()){if(!t||!e)return;ei(r);let o=jE(t);for(let[s,a]of Object.entries(qr))n>=qe[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function LE(t,e,n=Date.now()){if(!ie?.caps||!Tu)return;let r=rr.get(t);if(r)for(let[o,s]of Object.entries(qr)){let a=cg(t,o);if(a==null)continue;let i=r[o]||0;if(i<a*rg||i>=a)continue;let c=`warn:${t}:${o}:${qe[o]}`;if(!Kr.has(c)){Kr.add(c);try{let l=await Tu(e);if(!l)continue;await I(l,{type:"usage.cap.warning",actor:"system",provider:t,payload:{provider:t,window:o,limitUsd:a,spentUsd:ti(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function ug(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 ni(t,{source:e="local",updatedAt:n=Date.now()}={}){ie={caps:ug(t),updatedAt:n,source:e};try{await vu.mkdir(eg,{recursive:!0}),await vu.writeFile(ng(),JSON.stringify(ie,null,2))}catch{}return is()}function is(t=Date.now()){ei(t);let e=new Set([...rr.keys(),...Object.keys(ie?.caps||{})]),n={};for(let r of e){let o=rr.get(r);n[r]={weeklyUsd:ti(o?.weekly||0),monthlyUsd:ti(o?.monthly||0)}}return{caps:ie?.caps||{},configured:!!ie,seeded:Au,updatedAt:ie?.updatedAt??null,spent:n,windows:{weekly:{start:qe.weekly,resetsAt:ag(t)},monthly:{start:qe.monthly,resetsAt:ig(t)}},warnPct:rg}}async function dg({listTracks:t,getTrack:e}={}){Tu=e||null,ei();try{let o=JSON.parse(await vu.readFile(ng(),"utf8"));ie={caps:ug(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{ie=null}let n=Date.now();Ut.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Ln(o);Qw(o.provider||"unknown",s,o.ts??Date.now()),LE(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(qe.weekly,qe.monthly);for(let o of await t?.()||[]){let s;try{s=await L(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||Qw(a.provider||"unknown",Ln(a).usd,i,n)}}return Au=!0,is()}var ti=t=>Math.round(t*1e4)/1e4;import FE from"node:fs/promises";import UE from"node:os";import fg from"node:path";var ri=t=>new Promise(e=>setTimeout(e,t)),HE=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,WE=300,wg=[{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"}],qE=new Set([...wg.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"]),KE=[{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"}],gg=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),yg=[{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 bg(t){return t==="codex"?KE:t==="cursor"?yg:wg}function pg(t){let e=String(t||"").split(`
142
- `),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 mg(t){let e=String(t||"").split(`
143
- `),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 GE(t){let e=String(t||"").split(`
144
- `);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 hg(t){let e=String(t||"").split(`
145
- `),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 kg(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&&!gg.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=yg.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!qE.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function cs(t,e,{timeoutMs:n=HE}={}){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 ri(WE)}}async function VE(t,e){return await Et(t,`/model ${e.model}`,Ce("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function zE(t,e){await ca(t),await Et(t,"/model",{enter:!1}),await ri(250),await vt(t,["Enter"],{kind:"model-switch"});let n=await cs(t,pg);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await vt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${e.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=GE(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 cs(t,i=>mg(i)||!pg(i)&&o(i)&&{done:o(i)});if(!s)throw await vt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?gg.get(e.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await vt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await Et(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await cs(t,i=>!mg(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 JE(){let t=fg.join(process.env.CURSOR_CONFIG_DIR||fg.join(UE.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await FE.readFile(t,"utf8"))?.model||null}catch{return null}}async function XE(t,e){await ca(t),await ua(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await cs(t,hg),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 cs(t,hg,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await vt(t,["Down"],{kind:"model-switch"}),await ri(250)}if(!n||!s||!o(s)){await vt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await vt(t,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${e.filter}"`:"cursor model picker didn't open")}await vt(t,["Enter"],{kind:"model-switch"}),await ri(800);let a=await JE();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 xg(t,e,n){return t==="codex"?zE(e,n):t==="cursor"?XE(e,n):VE(e,n)}var Yt=t=>t||null;function si(t){let e=t||{},n=YE(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function YE(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Yt(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:Yt(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:Yt(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:Yt(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Yt(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:Yt(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:Yt(t.agent),payload:{...Gr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Yt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Gr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{change:"subtask-resumed",...Gr(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...Gr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Gr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Gr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var oi=Promise.resolve(),Eu=t=>(oi=oi.then(t).catch(()=>{}),oi),vg=()=>oi;function Tg(t,e){let n=si(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Eu(()=>I(r,n))}var Sg=new Set;async function Ag(t){if(!(!t?.id||Sg.has(t.id))){Sg.add(t.id);try{let e=await L(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=si(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(t,o)}}catch{}}}function Gr(t){let{type:e,at:n,...r}=t;return r}var _u=new Map;function _g(t,e,n){!n?.promptId||_u.get(t.id)===n.promptId||(_u.set(t.id,n.promptId),Eu(()=>I(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Yt(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 Iu(t,e,n,r={}){_u.delete(t.id),Eu(()=>I(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Yt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as RI}from"node:crypto";var Ig=/(?<!\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,Rg=/(?<!\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,$g=/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,Og=/(?<!\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,ZE=new RegExp(`\\b(?:${Ig}|${Rg}|${$g}|${Og})\\b`,"i"),QE=/\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,tI=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,eI=90;function nI(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function Mg(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of nI(n).reverse()){if(!ZE.test(r))continue;let o=r.endsWith("?")&&r.length<=eI&&tI.test(r);if(QE.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var Cg=["commit","rebase","push","pr"],rI=new RegExp(`\\b(?:${Ig})\\b`,"i"),oI=new RegExp(`\\b(?:${Og})\\b`,"i"),sI=new RegExp(`\\b(?:${Rg})\\b`,"i"),aI=new RegExp(`\\b(?:${$g})\\b`,"i"),iI=/\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 Ng(t){let e=String(t||""),n=[];rI.test(e)&&n.push("commit"),oI.test(e)&&n.push("rebase"),sI.test(e)&&n.push("push");let r=iI.test(e);!r&&aI.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 ai(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 cI=3e4;function Pg(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<=cI&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Dg(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}var Eg={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function Bg(t){let e=(Array.isArray(t)?t:[]).filter(r=>Eg[r]).map(r=>Eg[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 pI}from"node:child_process";import{promisify as mI}from"node:util";import lI from"node:fs";import Ru from"node:fs/promises";import Ug from"node:path";import uI from"node:os";var Hg=process.env.BOSTRAT_STATE_DIR||Ug.join(process.env.HOME||uI.tmpdir(),".bostrat"),ii=Ug.join(Hg,"github-metrics.json"),Wg=60*6e4,jg=5*6e4,dI=6e4,Vr=Date.now(),Ke=new Map,bn=null,or=0,Lg=0,ci=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(lI.readFileSync(ii,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Wg?(Vr=t.windowStart,Ke=new Map(Object.entries(t.byCaller||{})),bn=t.lastHour||null):ci(new Map(Object.entries(t.byCaller||{})))&&(bn={start:t.windowStart,total:ci(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var Fg=Promise.resolve();function qg(t,e=!1){if(!e&&t-Lg<dI)return;Lg=t;let n=JSON.stringify({windowStart:Vr,byCaller:Object.fromEntries(Ke),lastHour:bn});Fg=Fg.then(()=>Ru.mkdir(Hg,{recursive:!0})).then(()=>Ru.writeFile(`${ii}.tmp`,n)).then(()=>Ru.rename(`${ii}.tmp`,ii)).catch(()=>{})}function Kg(t){t-Vr<Wg||(bn={start:Vr,total:ci(Ke),byCaller:Object.fromEntries(Ke)},bn.total&&console.log(`[github] ${bn.total} calls last hour`,bn.byCaller),Vr=t,Ke=new Map,qg(t,!0))}function Ge(t="other",e=Date.now()){Kg(e),Ke.set(t,(Ke.get(t)||0)+1),qg(e)}function Gg(t=Date.now()){return Kg(t),{windowStart:Vr,total:ci(Ke),byCaller:Object.fromEntries(Ke),lastHour:bn,backoffUntil:or>t?or:null}}var fI=/rate limit|too quickly|abuse detection|HTTP 429/i;function kn(t){return fI.test(String(t?.message||t||""))}function xn(t,e=Date.now()){return or<=e&&console.warn(`[github] rate limited \u2014 backing off ${jg/6e4} min:`,String(t?.message||t||"").slice(0,200)),or=Math.max(or,e+jg),or}function li(t=Date.now()){return Math.max(0,or-t)}var Vg=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,ls=new Map;function ui(t,e,n){let{state:r,reason:o}=Hc(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of ls)a-c.at>=Vg()&&ls.delete(i);ls.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=ls.get(t);return s?Date.now()-s.at>=Vg()?(ls.delete(t),!1):s.busy:!1}async function $u(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await mt(t,{lines:_t}).catch(()=>"");if(!ui(t,e,r))return Op(e,r)?{working:!0,prompted:!1}:n;if(Ur(e,r)){let o=await an(t).catch(()=>r);if(nr({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function di(t,e){return(await $u(t,e)).working}var zg=mI(pI),Jg=[4e3,15e3,45e3],hI=2500,wI=5*6e4;async function Ou(t,e,{midTurn:n=di,pollMs:r=hI,capMs:o=wI}={}){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 gI=6e4,yI=9e4,bI=2e3,kI=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,xI=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,SI=/\s--web\b/,Xg=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Mu(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Yg(t){let e=String(t||""),n=e.match(kI);if(!n)return null;let r=Mu(e,n.index);return SI.test(r)?null:{repo:r.match(Xg)?.[1]??null}}var vI=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function Zg(t){let e=String(t||""),n=e.match(xI);if(!n)return null;let r=Mu(e,n.index),o=Mu(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("-")){vI.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Xg)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var TI="number,url,title,headRefName,createdAt";async function AI(t,e){let n=async a=>{Ge("observer");let{stdout:i}=await zg("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",TI],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...a}}).catch(c=>{throw kn(c)&&xn(c),c});return JSON.parse(i||"[]")},r=a=>(a||[]).filter(i=>Date.parse(i.createdAt)>=e-gI),o=await de({slug:t}),s=r(await n(o));return!s.length&&o.GH_TOKEN&&(s=r(await n({}).catch(()=>[]))),s.sort((a,i)=>Date.parse(i.createdAt)-Date.parse(a.createdAt))}async function _I(t,e){if(e)return[e];let n=[];for(let r of Qg(t)){let o=await ot(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Re().catch(()=>[])){let o=await ot(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Qg(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 EI(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await _I(t,e)){let a;try{a=await AI(s,n)}catch{continue}for(let i of a){let c=await Bt(s,i.number);if(!(c&&(c.trackId!==t.id||c.announced))){if(r&&i.headRefName===r)return{slug:s,pr:i};o.push({slug:s,pr:i})}}}return o.length?(o.sort((s,a)=>Date.parse(a.pr.createdAt)-Date.parse(s.pr.createdAt)),o[0]):null}var Sn=new Set;function ty(t,e,{delays:n=Jg,turnEnd:r}={}){let o=Yg(e.command);if(!o)return null;let s=!!Zg(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(Sn.has(a))return null;Sn.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await EI(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await Xt({repo:f,number:d.number,trackId:t.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await zo(f,d.number))?null:(await Ou(e.box,e.agent,r),await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{Sn.delete(a)}})();return c.catch(()=>Sn.delete(a)),c}var II="number,url,title,headRefName,state";function ey(t,e,{delays:n=Jg,turnEnd:r,openedWait:o}={}){let s=Zg(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(Sn.has(a))return null;Sn.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=Qg(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await ot(w).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,w)=>{let y=await Bt(c,m.number);return y&&y.trackId!==t.id?null:(y||await Xt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Jo(c,m.number),w&&((await L(t).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===m.number)||(await Ou(e.box,e.agent,r),await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(R=>setTimeout(R,m));let w=[];for(let R of f)try{Ge("observer");let{stdout:C}=await zg("gh",["pr","view",String(l),"--repo",R,"--json",II],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await de({slug:R})}}),v=JSON.parse(C||"null");v?.number&&w.push({slug:R,pr:v})}catch(C){kn(C)&&xn(C)}let y=w.find(R=>R.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let b=y.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(p=b,s.auto)return d(b,!0);continue}let S=await Bt(c,b.number);if(S&&S.trackId!==t.id)return null;S||await Xt({repo:c,number:b.number,trackId:t.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await Ou(e.box,e.agent,r);let E=await L(t).catch(()=>[]);if(E.some(R=>R.type==="pr.state.changed"&&R.payload?.change==="merged"&&R.payload?.number===b.number))return null;let O=R=>R.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="opened"&&C.payload?.number===b.number),nt=0;if(!O(E)){if(Yg(e.command)&&await zo(c,b.number)){let R=Date.now();nt=R+1,await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:R,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await Bt(c,b.number))?.announced){let R=o?.capMs??yI,C=o?.pollMs??bI,v=Date.now()+R;for(;Date.now()<v&&(await new Promise(D=>setTimeout(D,C)),!O(await L(t).catch(()=>[]))););}}return await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),nt),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await Tn(c,b.number).catch(()=>{}),await vn(c,b.number).catch(()=>{}),await wn(c,b.number,"merged"),b}return p?d(p,!1):null}finally{Sn.delete(a)}})();return i.catch(()=>Sn.delete(a)),i}function Nu(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function ny(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function ry(t){return`job|${t.box}|id:${t.id}`}var Cu=new Map,fi=new Map;async function Pu(t){return Cu.has(t.id)?Cu.get(t.id):(fi.has(t.id)||fi.set(t.id,L(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(Nu({role:o,sourceId:r.payload.sourceId})),n.keys.add(Nu({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(ny({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(ry({id:r.payload.id,box:r.boxId}));return Cu.set(t.id,n),n}).finally(()=>fi.delete(t.id))),fi.get(t.id))}async function oy(t,e){try{let n=(await Pu(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Nu(r);if(!n.has(o)&&(n.add(o),await I(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch)){let s=Mg(r.text);if(s){let{offered:a,toPr:i}=Ng(s),c;if(t.workspace?.kind==="multi"){let l=await us(t).catch(()=>[]);c=l.length?l.map(u=>({name:u.name,slug:u.repo??null})):void 0}await I(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:RI(),ts:r.ts,excerpt:s,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:a||void 0,toPr:i||void 0,repos:c}})}}}}catch{}}async function sy(t,e){try{let n=await Pu(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=ny(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await I(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),ty(t,o),ey(t,o))}}catch{}}async function ay(t,e){try{let n=await Pu(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=ry(o);n.keys.has(s)||(n.keys.add(s),await I(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var $I=/^Base directory for this skill:\s*(\S+)/,OI=/^Run the "([^"]+)" workflow\./,MI=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,CI=/^<local-command-(?:stdout|caveat)>/i,NI=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,PI=/^Continue from where you left off\.$/,DI=/^<task-notification>/i,BI=/<summary>([\s\S]*?)<\/summary>/i,jI=/<task-id>([\s\S]*?)<\/task-id>/i,LI=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,FI=/<status>([\s\S]*?)<\/status>/i,UI=/<result>([\s\S]*?)(?:<\/result>|$)/i;var Du=new Set(["model","clear","compact","login","exit","quit"]);function iy(t){return Du.has(String(t||"").replace(/^\//,"").toLowerCase())}var HI=/^\/([a-z0-9:_-]+)$/i;function pi(t){let e=String(t||"").trim();if(!e)return null;let n=e.match($I);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(OI);if(r)return{kind:"skill",name:r[1]};let o=e.match(MI);if(o){let a=o[1].toLowerCase();return Du.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(HI);if(s&&Du.has(s[1].toLowerCase()))return{kind:"suppress"};if(CI.test(e))return{kind:"suppress"};if(PI.test(e))return{kind:"suppress"};if(NI.test(e))return{kind:"suppress"};if(DI.test(e)){let a=c=>WI(e.match(c)?.[1].trim()||"")||null,i=a(UI);return{kind:"task",summary:a(BI)||"",taskId:a(jI),toolUseId:a(LI),status:a(FI)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function WI(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function cy(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":""}function ly(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var ct=t=>t.payload?.ts??t.ts;function qI(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 KI=new Set(["opened","ci-fixed","reconciled","pushed"]),uy=t=>`${t.repo??""}#${t.number}`;function GI(t,e,n){let r=uy(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 ds(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 h=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let A of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let _=A[0].split("/").pop().toLowerCase();f.has(_)||f.set(_,[]),f.get(_).push(g.payload?.ts??g.ts)}}let d=3*6e4,p=20*6e4,m=(g,h)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some(_=>_>=h-d&&_<=h+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let h=St(g.payload?.text);h&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(h))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let h=St(g.payload?.text);h&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(h))}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 h=g.payload.note,A={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:ct(g)};h.toolUseId&&i.set(h.toolUseId,A);let _=String(h.summary||"").match(u);_&&l.set(_[1],A)}let w=new Map,y=new Map,b=null,S=new Map,E=new Map,O=new Map,nt=new Map,R=new Map,C=new Map,v=new Map,D=()=>{for(let g of v.values())g.questions.length&&e.push(g);v.clear()},B=null,lt=6e4,q=new Map,T=(g,h)=>{let A=o.get(g);if(!A?.length)return!1;let _=St(h),J=s.get(g);return A.some(W=>{if(!_.startsWith(W))return!1;let it=St(_.slice(W.length));return!!it&&!!J?.has(it)})};for(let g of t||[]){let h=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(h.text||"").trim()&&!h.images?.length)break;{let A=h.note||pi(h.text);if(A){if(A.kind==="task"){if(A.toolUseId&&a.has(A.toolUseId))break;let _=String(A.summary||"").match(u);if(!A.toolUseId&&_&&c.has(_[1]))break}if(A.kind==="command"&&iy(A.command))break;A.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:ly(A),name:A.name??null,command:A.command??null,summary:A.summary??null,ts:ct(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${ct(g)}`)||g.source==="transcript"&&T(g.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:ct(g),box:g.boxId,agent:g.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(B=null);break}for(let A of S.values())A.status="stale";S.clear(),B&&B.box===g.boxId&&(e.push(qI(B)),B=null),e.push({id:g.id,kind:"message",role:"user",text:h.text,ts:ct(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...g.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;D();let A=(h.images||[]).filter(it=>!m(it,ct(g)));if(!String(h.text||"").trim()&&!A.length)break;if(g.provider==="cursor"){let it=`${g.boxId}|${h.text}`;if(n.has(it))break;n.add(it)}(!B||B.box===g.boxId)&&(B=null);let _=g.provider==="codex"?(h.tools||[]).filter(it=>!zl.has(it)&&!es.has(it)):h.tools||[],J=h.text,W=null;if(uu.has(g.provider)){let it=du(String(h.text||""));it&&(J=it.text,W={questions:it.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:J,ts:ct(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...W?{ask:W}:{},..._.length?{tools:_}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...A.length?{images:A}:{}}),W&&(B={id:g.id,groupId:fu(g.provider,h.sourceId||g.id,W.questions),provider:g.provider,box:g.boxId,questions:W.questions,ts:ct(g)});break}case"job.started":{if(!h.id)break;let A=ct(g),_=i.get(h.id);if(!_&&(h.kind||"agent")==="agent"&&h.name){let K=l.get(h.name);K&&K.ts>=A&&(_=K)}let J={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:_?_.status&&_.status!=="completed"?"failed":"done":"running",startedTs:A,finishedTs:_?.ts??null,summary:_?.summary??null,resultHead:_?.resultHead??null},W=q.get(g.boxId),it=W?.jobs[W.jobs.length-1]?.startedTs;if(W&&it!=null&&A-it<=lt)W.jobs.push(J);else{let K={id:g.id,kind:"job-dock",dockId:h.id,ts:A,box:g.boxId,agent:g.provider,jobs:[J]};q.set(g.boxId,K),e.push(K)}break}case"handoff.done":e.push(Te(g,"lead-handoff"));break;case"subtask.delegated":e.push({...Te(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(Te(g,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let _ of S.values())_.status="stale";S.clear()}let A=Te(g,`pr-${h.change}`);if(KI.has(h.change)&&h.number!=null){let _=GI(w,y,h);if(w.size>=2){for(let W of y.values())for(let it of W)it.grouped=!0;A.groupPrs=[...w.values()];let J=[A];for(let W of w.keys())y.set(W,J)}else{let J=y.get(_)??[];J.push(A),y.set(_,J)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let _=uy(h),J=b&&!b.complete||w.size>=2;if(w.has(_))w.delete(_),y.delete(_);else if(h.number!=null)for(let W of[...w.keys()])W.endsWith(`#${h.number}`)&&(w.delete(W),y.delete(W));if(J&&h.number!=null){(!b||b.complete)&&(b={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:ct(g),ts:ct(g),box:g.boxId,agent:g.provider},e.push(b)),b.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:ct(g)}),b.remaining=[...w.values()],b.complete=w.size===0;break}}e.push(A);break}case"pr.offered":{if(!h.offerId)break;for(let _ of S.values())_.status="stale";S.clear();let A={id:g.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:ct(g),box:g.boxId,agent:g.provider};S.set(h.offerId,A),E.set(h.offerId,A),e.push(A);break}case"pr.offer.answered":{let A=E.get(h.offerId);A&&(A.status=h.action||"dismiss",A.actions=Array.isArray(h.actions)?h.actions:null,A.notify=h.notify===!0,S.delete(h.offerId));break}case"box.state.changed":{let A=h.change==="model"?"model-change":h.change;if(!A)break;let _=Te(g,A);A==="model-change"&&(_.to=h.model),e.push(_);break}case"view.state.changed":{if(h.change!=="created"||h.restarted)break;e.push({id:g.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:ct(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:ct(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(h.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:ct(g),box:g.boxId,agent:g.provider});break}if(h.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:ct(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:ct(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"ticket.move.offered":{if(!h.offerId)break;for(let _ of O.values())_.key===h.key&&(_.status="stale",O.delete(_.offerId));let A={id:g.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:ct(g),box:g.boxId,agent:g.provider};O.set(h.offerId,A),nt.set(h.offerId,A),e.push(A);break}case"ticket.move.answered":{let A=nt.get(h.offerId);A&&(A.status=h.action==="moved"?"moved":"dismiss",O.delete(h.offerId));break}case"ticket.state.changed":{for(let A of O.values())A.key===h.key&&A.to===h.to&&(A.status="stale",O.delete(A.offerId));if(h.mode==="offer")break;e.push(Te(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(Te(g,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let _ of R.values())_.status="stale";R.clear();let A={id:g.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:ct(g),box:g.boxId,agent:g.provider};R.set(h.proposalId,A),C.set(h.proposalId,A),e.push(A);break}case"ticket.create.answered":{let A=C.get(h.proposalId);A&&(A.status=h.action==="created"?"created":"dismiss",A.createdCount=h.createdCount??null,A.failedCount=h.failedCount??null,R.delete(h.proposalId));break}case"ticket.created":{let A={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},_=h.proposalId?C.get(h.proposalId):null;if(_){_.created=A;break}e.push({id:g.id,kind:"ticket-created",...A,ts:ct(g),box:g.boxId,agent:g.provider});break}case"error":{if(!h.kind)break;e.push(Te(g,h.kind));break}case"usage.cap.hit":e.push(Te(g,"usage-cap-hit"));break;case"usage.cap.warning":e.push(Te(g,"usage-cap-warning"));break;case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:h.provider||g.provider,ts:ct(g),at:ct(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(B=null,h.control||!h.question)break;let A=h.groupId||h.promptId;if(!A)break;let _=v.get(A);_||(_={id:g.id,kind:"prompt",groupId:A,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:h.questionTotal??1,questions:[],ts:ct(g)},v.set(A,_));let J=h.questionIndex??0,W={index:J,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},it=_.questions.findIndex(K=>K.index===J&&St(K.question)===St(W.question));it>=0?_.questions[it]=W:_.questions.push(W),_.questions.sort((K,Y)=>K.index-Y.index),_.questionTotal=Math.max(_.questionTotal,h.questionTotal??1),_.questions.length>=_.questionTotal&&(e.push(_),v.delete(A));break}default:break}}return zI(e.sort((g,h)=>(g.ts??0)-(h.ts??0)))}var VI=2*6e4;function zI(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<=VI){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 Bu(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:JI(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??ds(t)).filter(o=>o.kind==="message");return lw(n,r)}function JI(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Br(t.command)?e:gn(t.command,t.description||"")}function Te(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:ct(t),ts:ct(t)}}var XI=12e4,YI=new Set(["archived","unarchived","opened"]);function dy(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)+XI,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&YI.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}function ZI(t){let e=[],n=null;for(let r of String(t||"").split(`
146
- `)){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 ju=new Map;async function QI(t,e){let n=`${t.id}:${e.name}`;if(!ju.has(n)){let r=await L(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);ju.set(n,o)}return ju.get(n)}async function fy(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Yn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await QI(t,e),o=ZI(await Ft(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await I(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:me(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await I(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}import mi from"node:fs";import Fu from"node:path";import tR from"node:os";var eR=process.env.BOSTRAT_STATE_DIR||Fu.join(process.env.HOME||tR.tmpdir(),".bostrat"),my=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,nR="https://cursor.com/api/dashboard/get-filtered-usage-events",py=100;async function rR({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(nR,{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:py})});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<py)break}return r}function oR(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 sR=5*6e4,An={at:0,rows:null,error:null},hi=()=>Fu.join(eR,"cursor-usage.json");function hy(){try{return JSON.parse(mi.readFileSync(hi(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function aR(t){try{mi.mkdirSync(Fu.dirname(hi()),{recursive:!0});let e=hi()+".tmp";mi.writeFileSync(e,JSON.stringify(t)),mi.renameSync(e,hi())}catch{}}var iR=5e3;async function cR({fetchImpl:t,now:e=Date.now()}={}){if(An.rows&&e-An.at<sR)return An.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=hy(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await rR({sinceMs:o,cookie:n,fetchImpl:t});An={at:e,rows:s.map(oR).filter(Boolean),error:null}}catch(s){An={at:e,rows:null,error:String(s?.message||s)}}return An.rows}var Uu=()=>({enabled:my(),lastPollAt:An.at||null,error:An.error}),Lu=new Map;async function lR(t,e){let n=`${t.id}:${e.name}`;if(!Lu.has(n)){let r=await L(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);Lu.set(n,o)}return Lu.get(n)}async function wy(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!my())return;let r=await cR({fetchImpl:n});if(!r||!r.length)return;let o=await yw(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await lR(t,e),a=hy(),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=me(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await I(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}aR({sinceMs:c,seen:[...i].slice(-iR)})}catch{}}import{execFile as mR}from"node:child_process";import{promisify as hR}from"node:util";import gy from"node:fs/promises";import yy from"node:path";import uR from"node:os";var by=process.env.BOSTRAT_STATE_DIR||yy.join(process.env.HOME||uR.tmpdir(),".bostrat"),dR=yy.join(by,"github-latency.ndjson"),fR=100,wi=[],Hu=new Map;function gi({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};wi.push(l),wi.length>fR&&wi.shift();let u=Hu.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)),Hu.set(t,u),pR(l),l}async function pR(t){try{await gy.mkdir(by,{recursive:!0}),await gy.appendFile(dR,JSON.stringify(t)+`
147
- `)}catch{}}function ky(){let t={};for(let[e,n]of Hu)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:wi.slice(-20)}}async function wR(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([Jr("git",["-C",t,"rev-parse","HEAD"]),Jr("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await I({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var Jr=hR(mR),gR={timeout:3e4,maxBuffer:8*1024*1024};async function ne(t,e,n={}){let{caller:r="prs",...o}=n,s=await de({cwd:t});Ge(r);try{let{stdout:a}=await Jr("gh",e,{cwd:t,...gR,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw kn(a)&&xn(a),a}}var yi=null;async function Ku(){if(yi&&Date.now()-yi.at<6e4)return yi.data;let t;try{Ge("auth");let e=await Jr("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 xo();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return yi={at:Date.now(),data:t},t}async function yR(t){try{let{stdout:e}=await Jr("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function Sy(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 Wu(t){let e=(t||[]).map(Sy),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 Gu="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",vy=Gu.replace("statusCheckRollup,","");function Vu(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Ty(t,e){if(!e)return null;let n=await ot(t);if(!n)return null;try{let r=await ne(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 Ay(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 _y(t,e){let n=await Ty(t,e);return n===null?{...Wu([]),unavailable:!0}:Wu(Ay(n))}function bi(t,e=Wu(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 bR=9e4,zr=new Map;function kR(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){zr.delete(s);continue}r.add(s),zr.has(s)||zr.set(s,n),o.mergeabilityPending=n-zr.get(s)<bR}for(let o of zr.keys())o.startsWith(`${t}#`)&&!r.has(o)&&zr.delete(o);return e}async function Xr(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await ne(n.path,["pr","view",String(r),"--json",Gu]);return bi(JSON.parse(o))}catch(o){if(!Vu(o))throw o;let s=await ne(n.path,["pr","view",String(r),"--json",vy]),a=JSON.parse(s);return bi(a,await _y(n.path,a.headRefOid))}}var xy=(t,e)=>t.number-e.number;async function ps(t,{caller:e="prs"}={}){try{let n=await ne(t,["pr","list","--json",Gu,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>bi(r)).sort(xy)}catch(n){if(!Vu(n))throw n;let r=await ne(t,["pr","list","--json",vy,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>bi(o,await _y(t,o.headRefOid))))).sort(xy)}}var xR=3e4,SR=1500,fs=new Map;function _e(){fs.clear()}async function vR(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 qa(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ga(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function Ey(){let t=await Ku();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Re())await yR(a.path)&&e.push(a);let n=Date.now(),r=li(n)>0,o=await Promise.all(e.map(async a=>{let i=fs.get(a.path),c=i?.pending?SR:xR;if(i&&(r||n-i.at<c))return i.entry;try{let l=await ot(a.path).catch(()=>null),u=kR(a.path,await ps(a.path,{caller:"tab"}),n);if(await vR(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&gi({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 fs.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:Ae(l)}}})),s=new Set(e.map(a=>a.path));for(let a of fs.keys())s.has(a)||fs.delete(a);return{auth:t,repos:o}}async function Ve(t){let e=(await Re()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function sr(t){return bo(t)}var ms=new Set(["squash","merge","rebase"]);async function Yr(t,e,{method:n="squash"}={}){let r=await Ve(t);if(!ms.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 de({cwd:r.path})).GH_TOKEN){let a=await uc().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await ne(r.path,s)}catch(a){throw new Error(Ae(a))}finally{_e()}return{ok:!0,number:o}}async function hs(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ne(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Ae(s))}finally{_e()}let o=await ot(n.path).catch(()=>null);return o&&await Wh(o,r).catch(()=>{}),{ok:!0,number:r}}async function zu(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ne(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Ae(o))}finally{_e()}return{ok:!0,number:r}}var TR=2500,AR=9e4,_R=t=>new Promise(e=>setTimeout(e,t));function Ju(t){return t.green&&!t.isDraft}function ER(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 Iy(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:Ju(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Ry(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var qu=new Map;function $y(){let t={};for(let[e,n]of qu)t[e]={...n};return t}async function Oy(t,{method:e="merge",onMerged:n=null}={}){let r=await Ve(t);if(!ms.has(e))throw new Error(`invalid merge method: ${e}`);if(qu.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await ps(r.path)).filter(Ju).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 qu.set(r.path,s),RR(s,o,{listPrs:()=>ps(r.path),merge:a=>Yr(r.path,a,{method:e}),sleep:_R,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function IR(t,e){let n=Date.now()+(e.settleMaxMs??AR),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(TR)}}async function RR(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 IR(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: ${Ae(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(!Ju(a)){t.skipped.push({number:a.number,title:a.title,reason:ER(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:Ae(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),_e()}}async function Xu(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Jr("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Ae(l)}`)}await wR(t);let o=n||"";if((await de({cwd:t})).GH_TOKEN){let l=await uc().catch(()=>null);l&&(o=`${o?`${o}
148
-
149
- `:""}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 ne(t,s)}catch(l){throw new Error(Ae(l))}finally{_e()}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 My(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await ne(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Ae(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Vu(i))throw i;let c=await ne(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Ty(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Ae(i));o=Ay(l).map(Sy).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=$R(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await ne(n.path,["run","view",c,"--log-failed"]).then(u=>OR(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function $R(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function OR(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
138
+ `;import{execFile as PE}from"node:child_process";import{promisify as DE}from"node:util";var ku=DE(PE),Zw=64*1024*1024;async function gt(t,e,{input:n}={}){let r=ku("git",["-C",t,...e],{maxBuffer:Zw});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function ei(t){if(!t)return!1;try{let{stdout:e}=await gt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Qw(t){let{stdout:e}=await gt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function BE(t){try{return await gt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function cs(t,e="bostrat: subtask snapshot"){let{stdout:n}=await gt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>ku("git",["-C",t,...a],{maxBuffer:Zw,env:o,...i});try{let a=await BE(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 ku("rm",["-f",r])}catch{}}}async function xu(t,e){let{stdout:n}=await gt(t,["status","--porcelain"]);return n.trim()?(await gt(t,["add","-A"]),await gt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function vu(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await gt(t,["status","--porcelain"]),r=n.split(`
139
+ `).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 Su(t,e){if(!e)return 0;try{let{stdout:n}=await gt(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Vr(t,e,n,r){return await gt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function tg(t,e){try{return await gt(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function eg(t,e,n){return await gt(t,["worktree","add",e,n]),e}async function ni(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await gt(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 gt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function ng(t){await gt(t,["init","-q"]),await gt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function te(t,e){try{await gt(t,["worktree","remove","--force",e])}catch{try{await gt(t,["worktree","prune"])}catch{}}}function jE(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 Tu(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Au(t,e,n,r){let{stdout:o}=await gt(t,Tu(["diff","--shortstat",e,n],r));return jE(o)}async function _u(t,e,n,r){let{stdout:o}=await gt(t,Tu(["diff","--name-only",e,n],r));return o.split(`
140
+ `).map(s=>s.trim()).filter(Boolean)}async function Eu(t,e,n,r){let{stdout:o}=await gt(t,Tu(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await bu(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await bu(t,o,["--3way","--whitespace=nowarn"]),a=await LE(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await bu(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function bu(t,e,n){try{return await gt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function LE(t){try{let{stdout:e}=await gt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
141
+ `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import Iu from"node:fs/promises";import og from"node:path";import FE from"node:os";var sg=process.env.BOSTRAT_STATE_DIR||og.join(process.env.HOME||FE.tmpdir(),".bostrat"),ag=()=>og.join(sg,"usage-caps.json"),ig=.8;function cg(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(e.getDate()-(e.getDay()+6)%7),e.getTime()}function lg(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(1),e.getTime()}function ug(t){let e=new Date(cg(t));return e.setDate(e.getDate()+7),e.getTime()}function dg(t){let e=new Date(lg(t));return e.setMonth(e.getMonth()+1),e.getTime()}var zr={weekly:{startOf:cg,nextStart:ug,capKey:"weeklyUsd"},monthly:{startOf:lg,nextStart:dg,capKey:"monthlyUsd"}},ae=null,$u=!1,qe={weekly:0,monthly:0},sr=new Map,Jr=new Set,Ru=null,UE=t=>{let e=sr.get(t);return e||(e={weekly:0,monthly:0},sr.set(t,e)),e};function oi(t=Date.now()){for(let[e,n]of Object.entries(zr)){let r=n.startOf(t);if(r!==qe[e]){qe[e]=r;for(let o of sr.values())o[e]=0;for(let o of Jr)o.includes(`:${e}:`)&&!o.endsWith(`:${r}`)&&Jr.delete(o)}}}function fg(t,e){let n=ae?.caps?.[t]?.[zr[e].capKey];return Number.isFinite(n)&&n>0?n:null}function ls(t,e=Date.now()){if(!t||!$u||!ae?.caps)return null;oi(e);let n=sr.get(t);for(let r of Object.keys(zr)){let o=fg(t,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:t,window:r,limitUsd:o,spentUsd:ri(s),resetsAt:zr[r].nextStart(e)}}return null}function pg(t,e){let n=`hit:${t}:${e.provider}:${e.window}:${qe[e.window]}`;return Jr.has(n)?!1:(Jr.add(n),!0)}function rg(t,e,n,r=Date.now()){if(!t||!e)return;oi(r);let o=UE(t);for(let[s,a]of Object.entries(zr))n>=qe[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function HE(t,e,n=Date.now()){if(!ae?.caps||!Ru)return;let r=sr.get(t);if(r)for(let[o,s]of Object.entries(zr)){let a=fg(t,o);if(a==null)continue;let i=r[o]||0;if(i<a*ig||i>=a)continue;let c=`warn:${t}:${o}:${qe[o]}`;if(!Jr.has(c)){Jr.add(c);try{let l=await Ru(e);if(!l)continue;await R(l,{type:"usage.cap.warning",actor:"system",provider:t,payload:{provider:t,window:o,limitUsd:a,spentUsd:ri(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function mg(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 si(t,{source:e="local",updatedAt:n=Date.now()}={}){ae={caps:mg(t),updatedAt:n,source:e};try{await Iu.mkdir(sg,{recursive:!0}),await Iu.writeFile(ag(),JSON.stringify(ae,null,2))}catch{}return us()}function us(t=Date.now()){oi(t);let e=new Set([...sr.keys(),...Object.keys(ae?.caps||{})]),n={};for(let r of e){let o=sr.get(r);n[r]={weeklyUsd:ri(o?.weekly||0),monthlyUsd:ri(o?.monthly||0)}}return{caps:ae?.caps||{},configured:!!ae,seeded:$u,updatedAt:ae?.updatedAt??null,spent:n,windows:{weekly:{start:qe.weekly,resetsAt:ug(t)},monthly:{start:qe.monthly,resetsAt:dg(t)}},warnPct:ig}}async function hg({listTracks:t,getTrack:e}={}){Ru=e||null,oi();try{let o=JSON.parse(await Iu.readFile(ag(),"utf8"));ae={caps:mg(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{ae=null}let n=Date.now();Ft.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Ce(o);rg(o.provider||"unknown",s,o.ts??Date.now()),HE(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(qe.weekly,qe.monthly);for(let o of await t?.()||[]){let s;try{s=await L(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||rg(a.provider||"unknown",Ce(a).usd,i,n)}}return $u=!0,us()}var ri=t=>Math.round(t*1e4)/1e4;import WE from"node:fs/promises";import qE from"node:os";import wg from"node:path";var ai=t=>new Promise(e=>setTimeout(e,t)),KE=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,GE=300,kg=[{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"}],VE=new Set([...kg.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"]),zE=[{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"}],xg=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),vg=[{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 Sg(t){return t==="codex"?zE:t==="cursor"?vg:kg}function gg(t){let e=String(t||"").split(`
142
+ `),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 yg(t){let e=String(t||"").split(`
143
+ `),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 JE(t){let e=String(t||"").split(`
144
+ `);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 bg(t){let e=String(t||"").split(`
145
+ `),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 Tg(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&&!xg.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=vg.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!VE.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function ds(t,e,{timeoutMs:n=KE}={}){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 ai(GE)}}async function XE(t,e){return await _t(t,`/model ${e.model}`,Me("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function YE(t,e){await fa(t),await _t(t,"/model",{enter:!1}),await ai(250),await St(t,["Enter"],{kind:"model-switch"});let n=await ds(t,gg);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await St(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=JE(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await _t(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await ds(t,i=>yg(i)||!gg(i)&&o(i)&&{done:o(i)});if(!s)throw await St(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?xg.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 St(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await _t(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await ds(t,i=>!yg(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 ZE(){let t=wg.join(process.env.CURSOR_CONFIG_DIR||wg.join(qE.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await WE.readFile(t,"utf8"))?.model||null}catch{return null}}async function QE(t,e){await fa(t),await ma(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await ds(t,bg),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 ds(t,bg,{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 St(t,["Down"],{kind:"model-switch"}),await ai(250)}if(!n||!s||!o(s)){await St(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await St(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 St(t,["Enter"],{kind:"model-switch"}),await ai(800);let a=await ZE();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 Ag(t,e,n){return t==="codex"?YE(e,n):t==="cursor"?QE(e,n):XE(e,n)}var Xt=t=>t||null;function ci(t){let e=t||{},n=tI(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function tI(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}};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:{...Xr(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",...Xr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Xt(t.agent),payload:{change:"subtask-resumed",...Xr(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",...Xr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Xr(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",...Xr(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 ii=Promise.resolve(),Mu=t=>(ii=ii.then(t).catch(()=>{}),ii),Eg=()=>ii;function Ig(t,e){let n=ci(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Mu(()=>R(r,n))}var _g=new Set;async function Rg(t){if(!(!t?.id||_g.has(t.id))){_g.add(t.id);try{let e=await L(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=ci(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function Xr(t){let{type:e,at:n,...r}=t;return r}var Ou=new Map;function $g(t,e,n){!n?.promptId||Ou.get(t.id)===n.promptId||(Ou.set(t.id,n.promptId),Mu(()=>R(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 Cu(t,e,n,r={}){Ou.delete(t.id),Mu(()=>R(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 MI}from"node:crypto";var Mg=/(?<!\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,Cg=/(?<!\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,Ng=/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,Pg=/(?<!\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,eI=new RegExp(`\\b(?:${Mg}|${Cg}|${Ng}|${Pg})\\b`,"i"),nI=/\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,rI=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,oI=90;function sI(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function Dg(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of sI(n).reverse()){if(!eI.test(r))continue;let o=r.endsWith("?")&&r.length<=oI&&rI.test(r);if(nI.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var Bg=["commit","rebase","push","pr"],aI=new RegExp(`\\b(?:${Mg})\\b`,"i"),iI=new RegExp(`\\b(?:${Pg})\\b`,"i"),cI=new RegExp(`\\b(?:${Cg})\\b`,"i"),lI=new RegExp(`\\b(?:${Ng})\\b`,"i"),uI=/\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 jg(t){let e=String(t||""),n=[];aI.test(e)&&n.push("commit"),iI.test(e)&&n.push("rebase"),cI.test(e)&&n.push("push");let r=uI.test(e);!r&&lI.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 li(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 dI=3e4;function Lg(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<=dI&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Fg(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}var Og={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function Ug(t){let e=(Array.isArray(t)?t:[]).filter(r=>Og[r]).map(r=>Og[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 wI}from"node:child_process";import{promisify as gI}from"node:util";import fI from"node:fs";import Nu from"node:fs/promises";import Kg from"node:path";import pI from"node:os";var Gg=process.env.BOSTRAT_STATE_DIR||Kg.join(process.env.HOME||pI.tmpdir(),".bostrat"),ui=Kg.join(Gg,"github-metrics.json"),Vg=60*6e4,Hg=5*6e4,mI=6e4,Yr=Date.now(),Ke=new Map,xn=null,ar=0,Wg=0,di=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(fI.readFileSync(ui,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Vg?(Yr=t.windowStart,Ke=new Map(Object.entries(t.byCaller||{})),xn=t.lastHour||null):di(new Map(Object.entries(t.byCaller||{})))&&(xn={start:t.windowStart,total:di(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var qg=Promise.resolve();function zg(t,e=!1){if(!e&&t-Wg<mI)return;Wg=t;let n=JSON.stringify({windowStart:Yr,byCaller:Object.fromEntries(Ke),lastHour:xn});qg=qg.then(()=>Nu.mkdir(Gg,{recursive:!0})).then(()=>Nu.writeFile(`${ui}.tmp`,n)).then(()=>Nu.rename(`${ui}.tmp`,ui)).catch(()=>{})}function Jg(t){t-Yr<Vg||(xn={start:Yr,total:di(Ke),byCaller:Object.fromEntries(Ke)},xn.total&&console.log(`[github] ${xn.total} calls last hour`,xn.byCaller),Yr=t,Ke=new Map,zg(t,!0))}function Ge(t="other",e=Date.now()){Jg(e),Ke.set(t,(Ke.get(t)||0)+1),zg(e)}function Xg(t=Date.now()){return Jg(t),{windowStart:Yr,total:di(Ke),byCaller:Object.fromEntries(Ke),lastHour:xn,backoffUntil:ar>t?ar:null}}var hI=/rate limit|too quickly|abuse detection|HTTP 429/i;function vn(t){return hI.test(String(t?.message||t||""))}function Sn(t,e=Date.now()){return ar<=e&&console.warn(`[github] rate limited \u2014 backing off ${Hg/6e4} min:`,String(t?.message||t||"").slice(0,200)),ar=Math.max(ar,e+Hg),ar}function fi(t=Date.now()){return Math.max(0,ar-t)}var Yg=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,fs=new Map;function pi(t,e,n){let{state:r,reason:o}=Vc(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of fs)a-c.at>=Yg()&&fs.delete(i);fs.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=fs.get(t);return s?Date.now()-s.at>=Yg()?(fs.delete(t),!1):s.busy:!1}async function Pu(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await mt(t,{lines:At}).catch(()=>"");if(!pi(t,e,r))return Np(e,r)?{working:!0,prompted:!1}:n;if(Kr(e,r)){let o=await cn(t).catch(()=>r);if(or({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function mi(t,e){return(await Pu(t,e)).working}var Zg=gI(wI),Qg=[4e3,15e3,45e3],yI=2500,bI=5*6e4;async function Du(t,e,{midTurn:n=mi,pollMs:r=yI,capMs:o=bI}={}){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 kI=6e4,xI=9e4,vI=2e3,SI=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,TI=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,AI=/\s--web\b/,ty=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Bu(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function ey(t){let e=String(t||""),n=e.match(SI);if(!n)return null;let r=Bu(e,n.index);return AI.test(r)?null:{repo:r.match(ty)?.[1]??null}}var _I=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function ny(t){let e=String(t||""),n=e.match(TI);if(!n)return null;let r=Bu(e,n.index),o=Bu(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("-")){_I.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(ty)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var EI="number,url,title,headRefName,createdAt";async function II(t,e){let n=async a=>{Ge("observer");let{stdout:i}=await Zg("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",EI],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...a}}).catch(c=>{throw vn(c)&&Sn(c),c});return JSON.parse(i||"[]")},r=a=>(a||[]).filter(i=>Date.parse(i.createdAt)>=e-kI),o=await de({slug:t}),s=r(await n(o));return!s.length&&o.GH_TOKEN&&(s=r(await n({}).catch(()=>[]))),s.sort((a,i)=>Date.parse(i.createdAt)-Date.parse(a.createdAt))}async function RI(t,e){if(e)return[e];let n=[];for(let r of ry(t)){let o=await et(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Ie().catch(()=>[])){let o=await et(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function ry(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 $I(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await RI(t,e)){let a;try{a=await II(s,n)}catch{continue}for(let i of a){let c=await Pt(s,i.number);if(!(c&&(c.trackId!==t.id||c.announced))){if(r&&i.headRefName===r)return{slug:s,pr:i};o.push({slug:s,pr:i})}}}return o.length?(o.sort((s,a)=>Date.parse(a.pr.createdAt)-Date.parse(s.pr.createdAt)),o[0]):null}var Tn=new Set;function oy(t,e,{delays:n=Qg,turnEnd:r}={}){let o=ey(e.command);if(!o)return null;let s=!!ny(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(Tn.has(a))return null;Tn.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await $I(t,o.repo,i).catch(()=>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 Yo(f,d.number))?null:(await Du(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{Tn.delete(a)}})();return c.catch(()=>Tn.delete(a)),c}var OI="number,url,title,headRefName,state";function sy(t,e,{delays:n=Qg,turnEnd:r,openedWait:o}={}){let s=ny(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(Tn.has(a))return null;Tn.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=ry(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await et(w).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,w)=>{let y=await Pt(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 Zo(c,m.number),w&&((await L(t).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===m.number)||(await Du(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(O=>setTimeout(O,m));let w=[];for(let O of f)try{Ge("observer");let{stdout:P}=await Zg("gh",["pr","view",String(l),"--repo",O,"--json",OI],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await de({slug:O})}}),S=JSON.parse(P||"null");S?.number&&w.push({slug:O,pr:S})}catch(P){vn(P)&&Sn(P)}let y=w.find(O=>O.pr.state==="MERGED")??(w.length===1?w[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 Pt(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 Du(e.box,e.agent,r);let E=await L(t).catch(()=>[]);if(E.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="merged"&&O.payload?.number===k.number))return null;let $=O=>O.some(P=>P.type==="pr.state.changed"&&P.payload?.change==="opened"&&P.payload?.number===k.number),z=0;if(!$(E)){if(ey(e.command)&&await Yo(c,k.number)){let O=Date.now();z=O+1,await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:O,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await Pt(c,k.number))?.announced){let O=o?.capMs??xI,P=o?.pollMs??vI,S=Date.now()+O;for(;Date.now()<S&&(await new Promise(C=>setTimeout(C,P)),!$(await L(t).catch(()=>[]))););}}return await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),z),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await _n(c,k.number).catch(()=>{}),await An(c,k.number).catch(()=>{}),await yn(c,k.number,"merged"),k}return p?d(p,!1):null}finally{Tn.delete(a)}})();return i.catch(()=>Tn.delete(a)),i}function Lu(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function ay(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function iy(t){return`job|${t.box}|id:${t.id}`}var ju=new Map,hi=new Map;async function Fu(t){return ju.has(t.id)?ju.get(t.id):(hi.has(t.id)||hi.set(t.id,L(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(Lu({role:o,sourceId:r.payload.sourceId})),n.keys.add(Lu({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(ay({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(iy({id:r.payload.id,box:r.boxId}));return ju.set(t.id,n),n}).finally(()=>hi.delete(t.id))),hi.get(t.id))}async function cy(t,e){try{let n=(await Fu(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Lu(r);if(!n.has(o)&&(n.add(o),await R(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch)){let s=Dg(r.text);if(s){let{offered:a,toPr:i}=jg(s),c;if(t.workspace?.kind==="multi"){let l=await ps(t).catch(()=>[]);c=l.length?l.map(u=>({name:u.name,slug:u.repo??null})):void 0}await R(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:MI(),ts:r.ts,excerpt:s,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:a||void 0,toPr:i||void 0,repos:c}})}}}}catch{}}async function ly(t,e){try{let n=await Fu(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=ay(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await R(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),oy(t,o),sy(t,o))}}catch{}}async function uy(t,e){try{let n=await Fu(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=iy(o);n.keys.has(s)||(n.keys.add(s),await R(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var CI=/^Base directory for this skill:\s*(\S+)/,NI=/^Run the "([^"]+)" workflow\./,PI=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,DI=/^<local-command-(?:stdout|caveat)>/i,BI=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,jI=/^Continue from where you left off\.$/,LI=/^<task-notification>/i,FI=/<summary>([\s\S]*?)<\/summary>/i,UI=/<task-id>([\s\S]*?)<\/task-id>/i,HI=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,WI=/<status>([\s\S]*?)<\/status>/i,qI=/<result>([\s\S]*?)(?:<\/result>|$)/i;var Uu=new Set(["model","clear","compact","login","exit","quit"]);function dy(t){return Uu.has(String(t||"").replace(/^\//,"").toLowerCase())}var KI=/^\/([a-z0-9:_-]+)$/i;function wi(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(CI);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(NI);if(r)return{kind:"skill",name:r[1]};let o=e.match(PI);if(o){let a=o[1].toLowerCase();return Uu.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(KI);if(s&&Uu.has(s[1].toLowerCase()))return{kind:"suppress"};if(DI.test(e))return{kind:"suppress"};if(jI.test(e))return{kind:"suppress"};if(BI.test(e))return{kind:"suppress"};if(LI.test(e)){let a=c=>GI(e.match(c)?.[1].trim()||"")||null,i=a(qI);return{kind:"task",summary:a(FI)||"",taskId:a(UI),toolUseId:a(HI),status:a(WI)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function GI(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function fy(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":""}function py(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var at=t=>t.payload?.ts??t.ts;function VI(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 zI=new Set(["opened","ci-fixed","reconciled","pushed"]),my=t=>`${t.repo??""}#${t.number}`;function JI(t,e,n){let r=my(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 ms(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 h=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let A of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let I=A[0].split("/").pop().toLowerCase();f.has(I)||f.set(I,[]),f.get(I).push(g.payload?.ts??g.ts)}}let d=3*6e4,p=20*6e4,m=(g,h)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some(I=>I>=h-d&&I<=h+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let h=xt(g.payload?.text);h&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(h))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let h=xt(g.payload?.text);h&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(h))}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 h=g.payload.note,A={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:at(g)};h.toolUseId&&i.set(h.toolUseId,A);let I=String(h.summary||"").match(u);I&&l.set(I[1],A)}let w=new Map,y=new Map,k=null,v=new Map,E=new Map,$=new Map,z=new Map,O=new Map,P=new Map,S=new Map,C=()=>{for(let g of S.values())g.questions.length&&e.push(g);S.clear()},D=null,it=6e4,ct=new Map,T=(g,h)=>{let A=o.get(g);if(!A?.length)return!1;let I=xt(h),J=s.get(g);return A.some(W=>{if(!I.startsWith(W))return!1;let st=xt(I.slice(W.length));return!!st&&!!J?.has(st)})};for(let g of t||[]){let h=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(h.text||"").trim()&&!h.images?.length)break;{let A=h.note||wi(h.text);if(A){if(A.kind==="task"){if(A.toolUseId&&a.has(A.toolUseId))break;let I=String(A.summary||"").match(u);if(!A.toolUseId&&I&&c.has(I[1]))break}if(A.kind==="command"&&dy(A.command))break;A.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:py(A),name:A.name??null,command:A.command??null,summary:A.summary??null,ts:at(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${at(g)}`)||g.source==="transcript"&&T(g.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:at(g),box:g.boxId,agent:g.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(D=null);break}for(let A of v.values())A.status="stale";v.clear(),D&&D.box===g.boxId&&(e.push(VI(D)),D=null),e.push({id:g.id,kind:"message",role:"user",text:h.text,ts:at(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...g.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;C();let A=(h.images||[]).filter(st=>!m(st,at(g)));if(!String(h.text||"").trim()&&!A.length)break;if(g.provider==="cursor"){let st=`${g.boxId}|${h.text}`;if(n.has(st))break;n.add(st)}(!D||D.box===g.boxId)&&(D=null);let I=g.provider==="codex"?(h.tools||[]).filter(st=>!Ql.has(st)&&!os.has(st)):h.tools||[],J=h.text,W=null;if(hu.has(g.provider)){let st=wu(String(h.text||""));st&&(J=st.text,W={questions:st.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:J,ts:at(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...W?{ask:W}:{},...I.length?{tools:I}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...A.length?{images:A}:{}}),W&&(D={id:g.id,groupId:gu(g.provider,h.sourceId||g.id,W.questions),provider:g.provider,box:g.boxId,questions:W.questions,ts:at(g)});break}case"job.started":{if(!h.id)break;let A=at(g),I=i.get(h.id);if(!I&&(h.kind||"agent")==="agent"&&h.name){let vt=l.get(h.name);vt&&vt.ts>=A&&(I=vt)}let J={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:I?I.status&&I.status!=="completed"?"failed":"done":"running",startedTs:A,finishedTs:I?.ts??null,summary:I?.summary??null,resultHead:I?.resultHead??null},W=ct.get(g.boxId),st=W?.jobs[W.jobs.length-1]?.startedTs;if(W&&st!=null&&A-st<=it)W.jobs.push(J);else{let vt={id:g.id,kind:"job-dock",dockId:h.id,ts:A,box:g.boxId,agent:g.provider,jobs:[J]};ct.set(g.boxId,vt),e.push(vt)}break}case"handoff.done":e.push(Se(g,"lead-handoff"));break;case"subtask.delegated":e.push({...Se(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(Se(g,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let I of v.values())I.status="stale";v.clear()}let A=Se(g,`pr-${h.change}`);if(zI.has(h.change)&&h.number!=null){let I=JI(w,y,h);if(w.size>=2){for(let W of y.values())for(let st of W)st.grouped=!0;A.groupPrs=[...w.values()];let J=[A];for(let W of w.keys())y.set(W,J)}else{let J=y.get(I)??[];J.push(A),y.set(I,J)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let I=my(h),J=k&&!k.complete||w.size>=2;if(w.has(I))w.delete(I),y.delete(I);else if(h.number!=null)for(let W of[...w.keys()])W.endsWith(`#${h.number}`)&&(w.delete(W),y.delete(W));if(J&&h.number!=null){(!k||k.complete)&&(k={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:at(g),ts:at(g),box:g.boxId,agent:g.provider},e.push(k)),k.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:at(g)}),k.remaining=[...w.values()],k.complete=w.size===0;break}}e.push(A);break}case"pr.offered":{if(!h.offerId)break;for(let I of v.values())I.status="stale";v.clear();let A={id:g.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:at(g),box:g.boxId,agent:g.provider};v.set(h.offerId,A),E.set(h.offerId,A),e.push(A);break}case"pr.offer.answered":{let A=E.get(h.offerId);A&&(A.status=h.action||"dismiss",A.actions=Array.isArray(h.actions)?h.actions:null,A.notify=h.notify===!0,v.delete(h.offerId));break}case"box.state.changed":{let A=h.change==="model"?"model-change":h.change;if(!A)break;let I=Se(g,A);A==="model-change"&&(I.to=h.model),e.push(I);break}case"view.state.changed":{if(h.change!=="created"||h.restarted)break;e.push({id:g.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:at(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:at(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(h.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:at(g),box:g.boxId,agent:g.provider});break}if(h.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:at(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:at(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:at(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:at(g),box:g.boxId,agent:g.provider});break}case"ticket.move.offered":{if(!h.offerId)break;for(let I of $.values())I.key===h.key&&(I.status="stale",$.delete(I.offerId));let A={id:g.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:at(g),box:g.boxId,agent:g.provider};$.set(h.offerId,A),z.set(h.offerId,A),e.push(A);break}case"ticket.move.answered":{let A=z.get(h.offerId);A&&(A.status=h.action==="moved"?"moved":"dismiss",$.delete(h.offerId));break}case"ticket.state.changed":{for(let A of $.values())A.key===h.key&&A.to===h.to&&(A.status="stale",$.delete(A.offerId));if(h.mode==="offer")break;e.push(Se(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:at(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(Se(g,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let I of O.values())I.status="stale";O.clear();let A={id:g.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:at(g),box:g.boxId,agent:g.provider};O.set(h.proposalId,A),P.set(h.proposalId,A),e.push(A);break}case"ticket.create.answered":{let A=P.get(h.proposalId);A&&(A.status=h.action==="created"?"created":"dismiss",A.createdCount=h.createdCount??null,A.failedCount=h.failedCount??null,O.delete(h.proposalId));break}case"ticket.created":{let A={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},I=h.proposalId?P.get(h.proposalId):null;if(I){I.created=A;break}e.push({id:g.id,kind:"ticket-created",...A,ts:at(g),box:g.boxId,agent:g.provider});break}case"error":{if(!h.kind)break;e.push(Se(g,h.kind));break}case"usage.cap.hit":e.push(Se(g,"usage-cap-hit"));break;case"usage.cap.warning":e.push(Se(g,"usage-cap-warning"));break;case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:h.provider||g.provider,ts:at(g),at:at(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(D=null,h.control||!h.question)break;let A=h.groupId||h.promptId;if(!A)break;let I=S.get(A);I||(I={id:g.id,kind:"prompt",groupId:A,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:h.questionTotal??1,questions:[],ts:at(g)},S.set(A,I));let J=h.questionIndex??0,W={index:J,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},st=I.questions.findIndex(vt=>vt.index===J&&xt(vt.question)===xt(W.question));st>=0?I.questions[st]=W:I.questions.push(W),I.questions.sort((vt,Ks)=>vt.index-Ks.index),I.questionTotal=Math.max(I.questionTotal,h.questionTotal??1),I.questions.length>=I.questionTotal&&(e.push(I),S.delete(A));break}default:break}}return YI(e.sort((g,h)=>(g.ts??0)-(h.ts??0)))}var XI=2*6e4;function YI(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<=XI){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 Hu(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:ZI(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??ms(t)).filter(o=>o.kind==="message");return pw(n,r)}function ZI(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Ur(t.command)?e:bn(t.command,t.description||"")}function Se(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:at(t),ts:at(t)}}var QI=12e4,tR=new Set(["archived","unarchived","opened"]);function hy(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)+QI,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&tR.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}function eR(t){let e=[],n=null;for(let r of String(t||"").split(`
146
+ `)){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 Wu=new Map;async function nR(t,e){let n=`${t.id}:${e.name}`;if(!Wu.has(n)){let r=await L(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);Wu.set(n,o)}return Wu.get(n)}async function wy(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Qn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await nR(t,e),o=eR(await Lt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await R(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:dn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await R(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}import gi from"node:fs";import Ku from"node:path";import rR from"node:os";var oR=process.env.BOSTRAT_STATE_DIR||Ku.join(process.env.HOME||rR.tmpdir(),".bostrat"),yy=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,sR="https://cursor.com/api/dashboard/get-filtered-usage-events",gy=100;async function aR({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(sR,{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:gy})});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<gy)break}return r}function iR(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 cR=5*6e4,En={at:0,rows:null,error:null},yi=()=>Ku.join(oR,"cursor-usage.json");function by(){try{return JSON.parse(gi.readFileSync(yi(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function lR(t){try{gi.mkdirSync(Ku.dirname(yi()),{recursive:!0});let e=yi()+".tmp";gi.writeFileSync(e,JSON.stringify(t)),gi.renameSync(e,yi())}catch{}}var uR=5e3;async function dR({fetchImpl:t,now:e=Date.now()}={}){if(En.rows&&e-En.at<cR)return En.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=by(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await aR({sinceMs:o,cookie:n,fetchImpl:t});En={at:e,rows:s.map(iR).filter(Boolean),error:null}}catch(s){En={at:e,rows:null,error:String(s?.message||s)}}return En.rows}var Gu=()=>({enabled:yy(),lastPollAt:En.at||null,error:En.error}),qu=new Map;async function fR(t,e){let n=`${t.id}:${e.name}`;if(!qu.has(n)){let r=await L(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);qu.set(n,o)}return qu.get(n)}async function ky(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!yy())return;let r=await dR({fetchImpl:n});if(!r||!r.length)return;let o=await vw(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await fR(t,e),a=by(),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=dn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await R(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}})}lR({sinceMs:c,seen:[...i].slice(-uR)})}catch{}}import{execFile as gR}from"node:child_process";import{promisify as yR}from"node:util";import xy from"node:fs/promises";import vy from"node:path";import pR from"node:os";var Sy=process.env.BOSTRAT_STATE_DIR||vy.join(process.env.HOME||pR.tmpdir(),".bostrat"),mR=vy.join(Sy,"github-latency.ndjson"),hR=100,bi=[],Vu=new Map;function ki({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};bi.push(l),bi.length>hR&&bi.shift();let u=Vu.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)),Vu.set(t,u),wR(l),l}async function wR(t){try{await xy.mkdir(Sy,{recursive:!0}),await xy.appendFile(mR,JSON.stringify(t)+`
147
+ `)}catch{}}function Ty(){let t={};for(let[e,n]of Vu)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:bi.slice(-20)}}async function bR(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([Qr("git",["-C",t,"rev-parse","HEAD"]),Qr("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await R({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var Qr=yR(gR),kR={timeout:3e4,maxBuffer:8*1024*1024};async function ee(t,e,n={}){let{caller:r="prs",...o}=n,s=await de({cwd:t});Ge(r);try{let{stdout:a}=await Qr("gh",e,{cwd:t,...kR,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw vn(a)&&Sn(a),a}}var xi=null;async function Xu(){if(xi&&Date.now()-xi.at<6e4)return xi.data;let t;try{Ge("auth");let e=await Qr("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 Ao();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return xi={at:Date.now(),data:t},t}async function xR(t){try{let{stdout:e}=await Qr("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function _y(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 zu(t){let e=(t||[]).map(_y),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 Yu="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Ey=Yu.replace("statusCheckRollup,","");function Zu(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Iy(t,e){if(!e)return null;let n=await et(t);if(!n)return null;try{let r=await ee(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 Ry(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 $y(t,e){let n=await Iy(t,e);return n===null?{...zu([]),unavailable:!0}:zu(Ry(n))}function vi(t,e=zu(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 vR=9e4,Zr=new Map;function SR(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Zr.delete(s);continue}r.add(s),Zr.has(s)||Zr.set(s,n),o.mergeabilityPending=n-Zr.get(s)<vR}for(let o of Zr.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Zr.delete(o);return e}async function to(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await ee(n.path,["pr","view",String(r),"--json",Yu]);return vi(JSON.parse(o))}catch(o){if(!Zu(o))throw o;let s=await ee(n.path,["pr","view",String(r),"--json",Ey]),a=JSON.parse(s);return vi(a,await $y(n.path,a.headRefOid))}}var Ay=(t,e)=>t.number-e.number;async function ws(t,{caller:e="prs"}={}){try{let n=await ee(t,["pr","list","--json",Yu,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>vi(r)).sort(Ay)}catch(n){if(!Zu(n))throw n;let r=await ee(t,["pr","list","--json",Ey,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>vi(o,await $y(t,o.headRefOid))))).sort(Ay)}}var TR=3e4,AR=1500,hs=new Map;function Ae(){hs.clear()}async function _R(t,e){if(!t)return e;for(let n of e){let r=await Pt(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 Va(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ja(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function Oy(){let t=await Xu();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Ie())await xR(a.path)&&e.push(a);let n=Date.now(),r=fi(n)>0,o=await Promise.all(e.map(async a=>{let i=hs.get(a.path),c=i?.pending?AR:TR;if(i&&(r||n-i.at<c))return i.entry;try{let l=await et(a.path).catch(()=>null),u=SR(a.path,await ws(a.path,{caller:"tab"}),n);if(await _R(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&ki({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 hs.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:Te(l)}}})),s=new Set(e.map(a=>a.path));for(let a of hs.keys())s.has(a)||hs.delete(a);return{auth:t,repos:o}}async function Ve(t){let e=(await Ie()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function ir(t){return So(t)}var gs=new Set(["squash","merge","rebase"]);async function eo(t,e,{method:n="squash"}={}){let r=await Ve(t);if(!gs.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 de({cwd:r.path})).GH_TOKEN){let a=await hc().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await ee(r.path,s)}catch(a){throw new Error(Te(a))}finally{Ae()}return{ok:!0,number:o}}async function ys(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ee(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Te(s))}finally{Ae()}let o=await et(n.path).catch(()=>null);return o&&await Vh(o,r).catch(()=>{}),{ok:!0,number:r}}async function Qu(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ee(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Te(o))}finally{Ae()}return{ok:!0,number:r}}var ER=2500,IR=9e4,RR=t=>new Promise(e=>setTimeout(e,t));function td(t){return t.green&&!t.isDraft}function $R(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 My(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:td(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Cy(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var Ju=new Map;function Ny(){let t={};for(let[e,n]of Ju)t[e]={...n};return t}async function Py(t,{method:e="merge",onMerged:n=null}={}){let r=await Ve(t);if(!gs.has(e))throw new Error(`invalid merge method: ${e}`);if(Ju.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await ws(r.path)).filter(td).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 Ju.set(r.path,s),MR(s,o,{listPrs:()=>ws(r.path),merge:a=>eo(r.path,a,{method:e}),sleep:RR,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function OR(t,e){let n=Date.now()+(e.settleMaxMs??IR),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(ER)}}async function MR(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 OR(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: ${Te(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(!td(a)){t.skipped.push({number:a.number,title:a.title,reason:$R(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:Te(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Ae()}}async function ed(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Qr("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Te(l)}`)}await bR(t);let o=n||"";if((await de({cwd:t})).GH_TOKEN){let l=await hc().catch(()=>null);l&&(o=`${o?`${o}
148
+
149
+ `:""}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 ee(t,s)}catch(l){throw new Error(Te(l))}finally{Ae()}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 Dy(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await ee(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Te(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Zu(i))throw i;let c=await ee(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Iy(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Te(i));o=Ry(l).map(_y).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=CR(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await ee(n.path,["run","view",c,"--log-failed"]).then(u=>NR(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function CR(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function NR(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
150
150
  `);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
151
151
  `+o.slice(-e).join(`
152
152
  `)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
153
- `+r.slice(-n)),r}function Ae(t){return String(t?.stderr||t?.message||t||"").trim().split(`
154
- `).find(n=>n.trim())||"gh failed"}var MR=6e3,ki=new Map;function Yu(t){let e=null;for(let n of t||[]){let r=n.payload?.ts??n.ts;n.type==="pr.offer.answered"?e=n.payload?.action==="pr"?{ts:r,offerId:n.payload?.offerId??null,box:n.boxId??null}:null:n.type==="pr.state.changed"&&n.payload?.change==="opened"&&e!=null&&r>=e.ts&&(e=null)}return e}async function Cy(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await L(t)}catch{return}if(Yu(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await di(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(ki.get(t.id)||0)<MR)return;ki.set(t.id,o);let s;try{s=await ps(t.cwd,{caller:"reconcile"})}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await ot(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Xt({repo:i,number:a.number,trackId:t.id,url:a.url,headRef:e,title:a.title,source:"reconciled"});if(!l&&!(c?.trackId===t.id&&await zo(i,a.number))){ki.delete(t.id);return}}await I(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e,...i?{repo:i}:{}}}).catch(()=>{}),ki.delete(t.id)}import CR from"node:fs/promises";import jy from"node:path";import NR from"node:os";import{randomUUID as Ly}from"node:crypto";var PR=process.env.BOSTRAT_STATE_DIR||jy.join(process.env.HOME||NR.tmpdir(),".bostrat"),Fy=jy.join(PR,"flows.json"),Ny=20,Py=80,Dy=500,By=80,ze=null,Zu=null;async function Je(){return ze||(Zu||=(async()=>{let t=!0;try{ze=JSON.parse(await CR.readFile(Fy,"utf8"))}catch{ze={flows:[]},t=!1}finally{Zu=null}if(ze.flows||=[],!t&&wt()){let{records:e,source:n}=await Ot("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&ze.flows.push(r.data);ze.flows.length&&await Zr().catch(()=>{})}}return ze})(),Zu)}async function Zr(){await Lt(Fy,ze)}var xi=t=>Dt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function Qu(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Uy({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>Ny)throw new Error(`steps must be an array of 1-${Ny}`);let r=await Hn({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>Dy)throw new Error(`${l}: caption over ${Dy} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>By)throw new Error(`${l}: title over ${By} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===d);if(p.length||(p=r.filter(S=>S.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let m=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(S=>S.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(S=>S.mockId===m.id),b=y>=0?o.splice(y,1)[0].id:Ly().slice(0,8);s.push({id:b,mockId:m.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function DR(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Py)throw new Error(`name over ${Py} chars`);return t.trim()}async function Hy({trackId:t}={}){let e=await Je();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function BR(t){let n=(await Je()).flows.find(r=>r.id===t);return n?{...n}:null}async function Wy(t){let e=await BR(t);if(!e)return null;let n=new Map((await Hn({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=Ne(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 qy({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=DR(e),i=await Je(),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 Uy({trackId:t,steps:n}),f=Date.now(),d={id:Ly().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 Zr(),await Qu(d,"created"),xi(d),{...d}}async function Ky(t,{steps:e,note:n}={}){let o=(await Je()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Uy({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 Zr(),await Qu(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),xi(o),{...o}}async function Gy(t){let e=await Je(),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 Zr(),await Qu(r,"deleted"),re("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Vy(t){return(await Je()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function zy(t){let n=(await Je()).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(),xi(r);return n.length&&await Zr(),n.length}async function Jy(t){let e=await Je(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await Zr();for(let r of n)re("flows",r.id).catch(()=>{})}return n.length}async function Xy(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Je();for(let r of n.flows)await xi(r);return{pushed:n.flows.length,source:"plane"}}import td from"node:fs/promises";import ws from"node:path";import{randomUUID as jR}from"node:crypto";var Zy=".bostrat/uploads",ed=t=>ws.join(t,Zy),Yy=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,LR=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),FR=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 nd(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return LR.has(e)?"image":FR.has(e)?"doc":null}function UR(t){let r=ws.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 Qy(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>Yy)throw new Error(`file too large \u2014 max ${Math.floor(Yy/(1024*1024))} MB`);let r=UR(e),o=ws.extname(r).slice(1),s=nd(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=jR().slice(0,8),i=ed(t);await td.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await td.writeFile(ws.join(i,c),n,{mode:384}),await td.chmod(ws.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${Zy}/${c}`,kind:s,size:n.length}}function tb(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var HR=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function rd(t){return t.role==="user"&&HR.test((t.text||"").trim())}function ar(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var KR=qR(WR),Xe=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),cb=U.join(Xe,"tracks.json"),GR=U.join(Xe,"events.jsonl"),ud=U.join(Xe,"workspaces"),VR=process.env.BOSTRAT_DEFAULT_REPO||null,Ai={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"}},_i=Object.keys(Ai);function zR(t,{skipPermissions:e=!0}={}){let n=Ai[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 lb(t,e,{skipPermissions:n=!0}={}){let r=Ai[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 As(t){let e=Ai[t];if(e)return process.env[e.envVar]||e.base}var _n={at:0,value:null};async function pd({maxAgeMs:t=6e4}={}){if(_n.value&&Date.now()-_n.at<t)return _n.value;let e=_n.value,n=await Promise.all(_i.map(async r=>{let o=await ia(As(r),{retries:[]});return o===yr?[r,e?.[r]??!0]:[r,o===aa]}));return _n.value=Object.fromEntries(n),_n.at=Date.now(),_n.value}async function Ei(t,e=!0){let n=zR(t,{skipPermissions:e});if(await ia(n)===gr){_n.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.":$c[r]?`Install it on this node: ${$c[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 Ii=".bostrat/handoff/latest.md";function JR(t,e=Ii){return`${t.trim()}
153
+ `+r.slice(-n)),r}function Te(t){return String(t?.stderr||t?.message||t||"").trim().split(`
154
+ `).find(n=>n.trim())||"gh failed"}var PR=6e3,Si=new Map;function nd(t){let e=null;for(let n of t||[]){let r=n.payload?.ts??n.ts;n.type==="pr.offer.answered"?e=n.payload?.action==="pr"?{ts:r,offerId:n.payload?.offerId??null,box:n.boxId??null}:null:n.type==="pr.state.changed"&&n.payload?.change==="opened"&&e!=null&&r>=e.ts&&(e=null)}return e}async function By(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await L(t)}catch{return}if(nd(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await mi(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Si.get(t.id)||0)<PR)return;Si.set(t.id,o);let s;try{s=await ws(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 Yo(i,a.number))){Si.delete(t.id);return}}await R(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e,...i?{repo:i}:{}}}).catch(()=>{}),Si.delete(t.id)}import DR from"node:fs/promises";import Hy from"node:path";import BR from"node:os";import{randomUUID as Wy}from"node:crypto";var jR=process.env.BOSTRAT_STATE_DIR||Hy.join(process.env.HOME||BR.tmpdir(),".bostrat"),qy=Hy.join(jR,"flows.json"),jy=20,Ly=80,Fy=500,Uy=80,ze=null,rd=null;async function Je(){return ze||(rd||=(async()=>{let t=!0;try{ze=JSON.parse(await DR.readFile(qy,"utf8"))}catch{ze={flows:[]},t=!1}finally{rd=null}if(ze.flows||=[],!t&&ht()){let{records:e,source:n}=await Rt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&ze.flows.push(r.data);ze.flows.length&&await no().catch(()=>{})}}return ze})(),rd)}async function no(){await jt(qy,ze)}var Ti=t=>Nt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function od(t,e,n={}){return R({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 Ky({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>jy)throw new Error(`steps must be an array of 1-${jy}`);let r=await qn({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>Fy)throw new Error(`${l}: caption over ${Fy} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Uy)throw new Error(`${l}: title over ${Uy} 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],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(v=>v.v===w)))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:Wy().slice(0,8);s.push({id:k,mockId:m.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function LR(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Ly)throw new Error(`name over ${Ly} chars`);return t.trim()}async function Gy({trackId:t}={}){let e=await Je();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function FR(t){let n=(await Je()).flows.find(r=>r.id===t);return n?{...n}:null}async function Vy(t){let e=await FR(t);if(!e)return null;let n=new Map((await qn({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=Ne(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 zy({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=LR(e),i=await Je(),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 Ky({trackId:t,steps:n}),f=Date.now(),d={id:Wy().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 no(),await od(d,"created"),Ti(d),{...d}}async function Jy(t,{steps:e,note:n}={}){let o=(await Je()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Ky({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 no(),await od(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Ti(o),{...o}}async function Xy(t){let e=await Je(),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 no(),await od(r,"deleted"),ne("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Yy(t){return(await Je()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function Zy(t){let n=(await Je()).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(),Ti(r);return n.length&&await no(),n.length}async function Qy(t){let e=await Je(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await no();for(let r of n)ne("flows",r.id).catch(()=>{})}return n.length}async function tb(){if(!ht())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Rt("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Je();for(let r of n.flows)await Ti(r);return{pushed:n.flows.length,source:"plane"}}import sd from"node:fs/promises";import bs from"node:path";import{randomUUID as UR}from"node:crypto";var nb=".bostrat/uploads",ad=t=>bs.join(t,nb),eb=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,HR=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),WR=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 id(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return HR.has(e)?"image":WR.has(e)?"doc":null}function qR(t){let r=bs.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 rb(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>eb)throw new Error(`file too large \u2014 max ${Math.floor(eb/(1024*1024))} MB`);let r=qR(e),o=bs.extname(r).slice(1),s=id(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=UR().slice(0,8),i=ad(t);await sd.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await sd.writeFile(bs.join(i,c),n,{mode:384}),await sd.chmod(bs.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${nb}/${c}`,kind:s,size:n.length}}function ob(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var KR=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function cd(t){return t.role==="user"&&KR.test((t.text||"").trim())}function cr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var zR=VR(GR),Xe=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),fb=U.join(Xe,"tracks.json"),JR=U.join(Xe,"events.jsonl"),hd=U.join(Xe,"workspaces"),XR=process.env.BOSTRAT_DEFAULT_REPO||null,Ii={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"}},Ri=Object.keys(Ii);function YR(t,{skipPermissions:e=!0}={}){let n=Ii[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 pb(t,e,{skipPermissions:n=!0}={}){let r=Ii[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 Is(t){let e=Ii[t];if(e)return process.env[e.envVar]||e.base}var In={at:0,value:null};async function yd({maxAgeMs:t=6e4}={}){if(In.value&&Date.now()-In.at<t)return In.value;let e=In.value,n=await Promise.all(Ri.map(async r=>{let o=await da(Is(r),{retries:[]});return o===xr?[r,e?.[r]??!0]:[r,o===ua]}));return In.value=Object.fromEntries(n),In.at=Date.now(),In.value}async function $i(t,e=!0){let n=YR(t,{skipPermissions:e});if(await da(n)===kr){In.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.":Pc[r]?`Install it on this node: ${Pc[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 Oi=".bostrat/handoff/latest.md";function ZR(t,e=Oi){return`${t.trim()}
155
155
 
156
- (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 XR=/\(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 eb(t){return t.role==="user"&&XR.test((t.text||"").trim())}var Si=".bostrat/handoff/subtask-result.md",ys=".bostrat/handoff/lead-context.md",YR=(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 ${ys} \u2014 read that first for context. Your scoped task:
156
+ (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 QR=/\(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 sb(t){return t.role==="user"&&QR.test((t.text||"").trim())}var Ai=".bostrat/handoff/subtask-result.md",xs=".bostrat/handoff/lead-context.md",t$=(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 ${xs} \u2014 read that first for context. Your scoped task:
157
157
 
158
158
  ${t}
159
159
 
160
- 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 ${Si} (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 ${ys} \u2014 read that first for context. Your scoped task:
160
+ 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 ${Ai} (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 ${xs} \u2014 read that first for context. Your scoped task:
161
161
 
162
162
  ${t}
163
163
 
164
- 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 ${Si} 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.`,ZR=(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."),Ee=null,od=null;async function H(){return Ee||(od||=(async()=>{try{Ee=JSON.parse(await F.readFile(cb,"utf8"))}catch{Ee={tracks:[]}}finally{od=null}return Ee})(),od)}async function X(){await Lt(cb,Ee)}var tt=(t,e)=>t.tracks.find(n=>n.id===e)||null,sd=Promise.resolve();function QR(t,e){let n=JSON.stringify({trackId:t,...e})+`
165
- `;return sd=sd.then(()=>F.mkdir(Xe,{recursive:!0})).then(()=>F.appendFile(GR,n)).catch(()=>{}),sd}var dt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),QR(t.id,r),Tg(t,r),r};xp(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 X().catch(()=>{}))});var ad=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),t$=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function ub(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=>t$.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 db(t,e,n,{kind:r}={}){let o=await rt(e);if(!o)throw new Error(`box "${e}" is not running`);let s=As(o.agent);await ln({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await Et(e,n,{...Ce(o.agent),kind:r||"orchestration"}),await G({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await H(),i=tt(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);dt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await X()}var cr=new Set(["merged","conflict","canceled","failed","handed-off"]),nb=new Map;async function e$(t){let e=U.join(ka(t),`${t.id}.ndjson`),n;try{n=await F.stat(e)}catch{return null}let r=nb.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await L(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return nb.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function n$(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:$u(n.name,n.agent)}async function Ye(t,e=null){let n=e??new Map((await Oe()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=cr.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([e$(t),n$(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:vi.has(t.id)}}var r$=5e3;function o$(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-r$,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 fb(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(d=>d.box.sessionId),a=hp(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=o$(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 ap(d.box,d.sessionId),n=!0}}return n}async function at(){let t=await H();await fb(t)&&await X();let e=new Map((await Oe()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>Ye(n,e)))}async function pb(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 mb(t){let e=go(t);if(!e)return null;let n=await H();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&df(o.hookTokenHash,e))return{track:r,box:o};return null}async function et(t){let e=await H(),n=tt(e,t);return n?(await fb(e)&&await X(),Ye(n)):null}var s$={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function rb(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(s$[t.agent]||[]).some(n=>n.test(e))}var a$=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function ob(t){let e=(t.text||"").trim();return e?a$.some(n=>n.test(e)):!1}var i$=10*6e4;async function md(t,e={}){let n=await et(t);if(!n)return null;let r=new Set([...n.events.filter(k=>k.type==="orchestration-prompt"&&k.prompt).map(k=>String(k.prompt).trim()),...n.events.filter(k=>k.type==="said"&&k.text).map(k=>String(k.text).trim())]),o=_s(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=sl(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Wm(n),...l],d={windows:[...rm(n),...om(n,va),...a,...u]},m=(await Promise.all(n.boxes.map(k=>eu(k,e)))).flat(),w=m.map(k=>{let P={kind:"message",...k};if(P.role==="assistant"&&(P.text||"").trim()&&P.tools?.length){let $t=ns(P.agent).cliTools,Qt=P.tools.filter(Tt=>!$t.has(Tt));return{...P,tools:Qt.length?Qt:void 0}}return P}).map(k=>{if(k.role!=="user")return k;let P=pi(k.text);return P?P.kind==="suppress"?null:{...k,note:P,text:cy(P)}:k}).filter(Boolean).filter(k=>!(k.role==="user"&&r.has((k.text||"").trim()))).filter(k=>!rb(k)).filter(k=>!ob(k)).filter(k=>!eb(k)).filter(k=>!rd(k)).filter(k=>k.role!=="assistant"||!!(k.text||"").trim()||!!k.images?.length).filter(k=>!sm(k,d)),y=w.filter(k=>k.failed&&k.text===Jl);if(y.length){let k=new Map;for(let P of y){if(!k.has(P.box)){let Tt=[...(await mt(P.box,{lines:_t}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(cf=>cf[1]);k.set(P.box,Tt.length?Tt[Tt.length-1]:null)}let $t=k.get(P.box);$t&&(P.text=`\u25A0 ${$t}`)}}let b=await Promise.all(n.boxes.map(k=>Iw(k,e)));await ay(n,b.flat()),await oy(n,w),await Ag(n),await Xm(n,w).catch(()=>{});for(let k of n.boxes)k.agent==="codex"&&k.state==="live"&&await fy(n,k),k.agent==="cursor"&&k.state==="live"&&await wy(n,k);let S=await Promise.all(n.boxes.map(k=>Ew(k,e)));await sy(n,S.flat().filter(k=>!ad(k.box,k.ts,u)));let E=await L(n),O=c&&i.viewId?i.viewId:null,nt=Pg(E),R=ds(E).filter(k=>!(k.kind==="message"&&(rb(k)||ob(k)||eb(k)||rd(k)))&&!(k.kind==="view-card"&&O&&k.viewId===O)),C=Dg(R,nt),v=R.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&!C.has(k)&&ad(k.box,k.ts,nt))),D=Bu(E,v).map(k=>({kind:"command-batch",id:k.id,ts:k.pending?k.ts:(k.anchorTs??k.ts)-1,anchorTs:k.anchorTs,anchorBox:k.anchorBox,agent:k.agent,boxRole:k.boxRole,commands:k.commands,pending:k.pending})).filter(k=>!ad(k.anchorBox,k.anchorTs??k.ts,u)),B=dy([...v,...D].filter(k=>k.ts!=null).sort((k,P)=>k.ts-P.ts)),lt=c$(n),q=lt.size?B.filter(k=>k.kind==="event"||!lt.has(k.box??k.anchorBox)):B,T=n.boxes.find(k=>k.name===n.leadBox)||null,g=T?await Oo(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!g&&T){let k=[...E].reverse().find(P=>P.type==="context.usage"&&P.boxId===T.name);k?.payload?.window&&(g={pct:k.payload.pct??(k.payload.occupancy!=null?k.payload.occupancy/k.payload.window:null),occupancy:k.payload.occupancy??null,window:k.payload.window,windowKnown:!0,model:k.payload.model||null})}let h=[...m].reverse().find(k=>k.role==="assistant"&&k.model),A=h?.model||null,_=[...n.events||[]].reverse().find(k=>k.type==="model-change"),J=g?.model||A;_&&(!h?.ts||_.at>=h.ts)&&(J=_.to);let W=T?await mt(T.name,{lines:_t}).catch(()=>""):"",it=T&&Ur(T.agent,W)?await an(T.name).catch(()=>W):W,Pt=T?nr(T,it):null;!Pt&&T&&!Co(T.agent,W)&&!dd(n.id).length&&(Pt=pu(q,{leadBoxId:T.name,provider:T.agent,isAnswered:k=>E.some(P=>P.type==="prompt.answered"&&P.payload?.promptId===k)})),Pt&&_g(n,T,Pt);let K=q.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&k.ask&&!String(k.text||"").trim()&&!k.images?.length)),$=!!T&&ui(T.name,T.agent,W)&&!Pt;T&&!T.readyAt&&($||Pt||W&&ha(T.agent,W))&&(T.readyAt=Date.now(),await X());let z=!!T&&!T.readyAt&&Date.now()-(n.createdAt||0)<i$;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&ha(T.agent,W)&&!Pt){let k=await H(),P=tt(k,n.id);P?.setup&&!P.setup.connectedAt&&(P.setup.connectedAt=Date.now(),n.setup.connectedAt=P.setup.connectedAt,await I(P,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:P.setup.connectedAt}}).catch(()=>{}),await X())}let xt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,ht=null;if(c){let k=Date.now()-i.startedAt;if(i.viewId&&!$){let P=$a(n.id);P&&(dt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:P.finishedAt}),await X())}else if(Pt||k>Um&&!$&&!i.viewId||k>ol){let P=Pt?"needs-input":k>ol?"timeout":"no-view";$a(n.id,{error:P})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:P}}).catch(()=>{})}else ht={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:K,context:g?{pct:g.pct,occupancy:g.occupancy,window:g.window,windowKnown:g.windowKnown,model:g.model}:null,models:{current:J,options:bg(T?.agent)},pendingPrompt:Pt,working:$,stopping:g$(n.id),leadStarting:z,handoff:xt,view:ht,queued:dd(n.id).filter(k=>k.kind!=="ship-notify"&&k.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function c$(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 hb(t,e){let n=await et(t);if(!n)return null;let r=ao(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 L(n),a=ds(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Bu(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=ui(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function hd(t,{promptId:e,answers:n,text:r}={}){let o=tt(await H(),t);if(!o)throw new Error(`track "${t}" not found`);$i(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 rt(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await cu(s);if(i||(i=await l$(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 vt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=Gw(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await le(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=Lw(i,n||[]);await vt(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=Ip(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],w=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));Iu(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:w})}return await G({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(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 G({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Iu(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function l$(t,e){if(!e)return null;let n=await L(t).catch(()=>[]);return pu(ds(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function wb(t,{promptId:e,questionIndex:n}={}){let r=tt(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 rt(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=Fw(a,n);return c.length?(await vt(r.leadBox,c,{kind:"navigate-prompt"}),await G({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}var u$=1500,bs=new Map;function wd(t){let e=bs.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},bs.set(t,e)),e}function dd(t){return(bs.get(t)?.items??[]).map(e=>({...e}))}function Ri(t){let e=bs.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 gb(t){let e=Ri(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=tt(await H(),t);if(o)return r=no(o,n),await X(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function yb(t){let e=wd(t);e.timer||(e.timer=setInterval(()=>d$(t).catch(()=>{}),u$))}async function d$(t){let e=bs.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=tt(await H(),t);if(!n||n.archivedAt){Ri(t);return}let r=_s(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){ar(e,!1);return}let o=await rt(n.leadBox);if(!o){ar(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(as(s?.agent)){ar(e,!1);return}let a=await mt(n.leadBox,{lines:_t}).catch(()=>"");if(ma(s?.agent,a)){ar(e,!1);return}if(Ur(s?.agent,a)){let c=await an(n.leadBox).catch(()=>a);if(nr(s,c)){ar(e,!1);return}}if(!ar(e,!0))return;let i=e.items.shift();e.idleSince=null,await kb(n,s,o,i.text,i.kind??null,i.attachments??null),await X()}finally{e.flushing=!1}}}async function bb(t,e,n,r){let o=as(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(no(t,[n]),await X()),lg(t.id,o))try{await I(t,{type:"usage.cap.hit",actor:"system",provider:e?.agent??null,boxId:t.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${e?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function kb(t,e,n,r,o=null,s=null){await bb(t,e,r,o);let a=ks.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=JR(r,t.pendingHandoffHydrate.artifact||Ii),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=tb(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
164
+ 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 ${Ai} 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.`,e$=(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,ld=null;async function H(){return _e||(ld||=(async()=>{try{_e=JSON.parse(await F.readFile(fb,"utf8"))}catch{_e={tracks:[]}}finally{ld=null}return _e})(),ld)}async function V(){await jt(fb,_e)}var Z=(t,e)=>t.tracks.find(n=>n.id===e)||null,ud=Promise.resolve();function n$(t,e){let n=JSON.stringify({trackId:t,...e})+`
165
+ `;return ud=ud.then(()=>F.mkdir(Xe,{recursive:!0})).then(()=>F.appendFile(JR,n)).catch(()=>{}),ud}var dt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),n$(t.id,r),Ig(t,r),r};Tp(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 dd=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),r$=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function mb(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=>r$.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 hb(t,e,n,{kind:r}={}){let o=await tt(e);if(!o)throw new Error(`box "${e}" is not running`);let s=Is(o.agent);await un({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await _t(e,n,{...Me(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 ur=new Set(["merged","conflict","canceled","failed","handed-off"]),ab=new Map;async function o$(t){let e=U.join(Ta(t),`${t.id}.ndjson`),n;try{n=await F.stat(e)}catch{return null}let r=ab.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await L(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return ab.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function s$(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Pu(n.name,n.agent)}async function Ye(t,e=null){let n=e??new Map((await $e()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=ur.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([o$(t),s$(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:_i.has(t.id)}}var a$=5e3;function i$(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-a$,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 wb(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=yp(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=i$(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 lp(d.box,d.sessionId),n=!0}}return n}async function ot(){let t=await H();await wb(t)&&await V();let e=new Map((await $e()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>Ye(n,e)))}async function gb(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 yb(t){let e=xo(t);if(!e)return null;let n=await H();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&mf(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 wb(e)&&await V(),Ye(n)):null}var c$={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function ib(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(c$[t.agent]||[]).some(n=>n.test(e))}var l$=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function cb(t){let e=(t.text||"").trim();return e?l$.some(n=>n.test(e)):!1}var u$=10*6e4;async function bd(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=Rs(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=[...Vm(n),...l],d={windows:[...am(n),...im(n,Ea),...a,...u]},m=(await Promise.all(n.boxes.map(b=>au(b,e)))).flat(),w=m.map(b=>{let _={kind:"message",...b};if(_.role==="assistant"&&(_.text||"").trim()&&_.tools?.length){let lt=ss(_.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 _=wi(b.text);return _?_.kind==="suppress"?null:{...b,note:_,text:fy(_)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!ib(b)).filter(b=>!cb(b)).filter(b=>!sb(b)).filter(b=>!cd(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!cm(b,d)),y=w.filter(b=>b.failed&&b.text===tu);if(y.length){let b=new Map;for(let _ of y){if(!b.has(_.box)){let bt=[...(await mt(_.box,{lines:At}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Dt=>Dt[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=>Mw(b,e)));await uy(n,k.flat()),await cy(n,w),await Rg(n),await th(n,w).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await wy(n,b),b.agent==="cursor"&&b.state==="live"&&await ky(n,b);let v=await Promise.all(n.boxes.map(b=>Ow(b,e)));await ly(n,v.flat().filter(b=>!dd(b.box,b.ts,u)));let E=await L(n),$=c&&i.viewId?i.viewId:null,z=Lg(E),O=ms(E).filter(b=>!(b.kind==="message"&&(ib(b)||cb(b)||sb(b)||cd(b)))&&!(b.kind==="view-card"&&$&&b.viewId===$)),P=Fg(O,z),S=O.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!P.has(b)&&dd(b.box,b.ts,z))),C=Hu(E,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=>!dd(b.anchorBox,b.anchorTs??b.ts,u)),D=hy([...S,...C].filter(b=>b.ts!=null).sort((b,_)=>b.ts-_.ts)),it=d$(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 Po(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!g&&T){let b=[...E].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 h=[...m].reverse().find(b=>b.role==="assistant"&&b.model),A=h?.model||null,I=[...n.events||[]].reverse().find(b=>b.type==="model-change"),J=g?.model||A;I&&(!h?.ts||I.at>=h.ts)&&(J=I.to);let W=T?await mt(T.name,{lines:At}).catch(()=>""):"",st=T&&Kr(T.agent,W)?await cn(T.name).catch(()=>W):W,Ct=T?or(T,st):null;!Ct&&T&&!Bo(T.agent,W)&&!wd(n.id).length&&(Ct=yu(ct,{leadBoxId:T.name,provider:T.agent,isAnswered:b=>E.some(_=>_.type==="prompt.answered"&&_.payload?.promptId===b)})),Ct&&$g(n,T,Ct);let vt=ct.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),pr=!!T&&pi(T.name,T.agent,W)&&!Ct;T&&!T.readyAt&&(pr||Ct||W&&ba(T.agent,W))&&(T.readyAt=Date.now(),await V());let Gs=!!T&&!T.readyAt&&Date.now()-(n.createdAt||0)<u$;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&ba(T.agent,W)&&!Ct){let b=await H(),_=Z(b,n.id);_?.setup&&!_.setup.connectedAt&&(_.setup.connectedAt=Date.now(),n.setup.connectedAt=_.setup.connectedAt,await R(_,{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,B=null;if(c){let b=Date.now()-i.startedAt;if(i.viewId&&!pr){let _=Ca(n.id);_&&(dt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:_.finishedAt}),await V())}else if(Ct||b>Km&&!pr&&!i.viewId||b>ll){let _=Ct?"needs-input":b>ll?"timeout":"no-view";Ca(n.id,{error:_})&&await R(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:_}}).catch(()=>{})}else B={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:vt,context:g?{pct:g.pct,occupancy:g.occupancy,window:g.window,windowKnown:g.windowKnown,model:g.model}:null,models:{current:J,options:Sg(T?.agent)},pendingPrompt:Ct,working:pr,stopping:k$(n.id),leadStarting:Gs,handoff:G,view:B,queued:wd(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function d$(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 bb(t,e){let n=await Q(t);if(!n)return null;let r=uo(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 L(n),a=ms(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Hu(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:At}).catch(()=>"");u=pi(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function kd(t,{promptId:e,answers:n,text:r}={}){let o=Z(await H(),t);if(!o)throw new Error(`track "${t}" not found`);Ci(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 pu(s);if(i||(i=await f$(o,s)),!i)throw new Error("no pending prompt to answer");if(e&&e!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let d=typeof r=="string"?r.trim():"";if(!d)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(d)||d.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await _t(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await St(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=Xw(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await ce(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=Ww(i,n||[]);await St(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=Op(i,n||[]);await _t(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:d,count:1}}if(i.answerMode==="say"){let d=i.questions||[];for(let p=0;p<d.length;p++){let m=d[p],w=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));Cu(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:w})}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(()=>{}),Cu(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function f$(t,e){if(!e)return null;let n=await L(t).catch(()=>[]);return yu(ms(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function kb(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 pu(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=qw(a,n);return c.length?(await St(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 p$=1500,vs=new Map;function xd(t){let e=vs.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},vs.set(t,e)),e}function wd(t){return(vs.get(t)?.items??[]).map(e=>({...e}))}function Mi(t){let e=vs.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 xb(t){let e=Mi(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=ao(o,n),await V(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function vb(t){let e=xd(t);e.timer||(e.timer=setInterval(()=>m$(t).catch(()=>{}),p$))}async function m$(t){let e=vs.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){Mi(t);return}let r=Rs(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){cr(e,!1);return}let o=await tt(n.leadBox);if(!o){cr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(ls(s?.agent)){cr(e,!1);return}let a=await mt(n.leadBox,{lines:At}).catch(()=>"");if(ya(s?.agent,a)){cr(e,!1);return}if(Kr(s?.agent,a)){let c=await cn(n.leadBox).catch(()=>a);if(or(s,c)){cr(e,!1);return}}if(!cr(e,!0))return;let i=e.items.shift();e.idleSince=null,await Tb(n,s,o,i.text,i.kind??null,i.attachments??null),await V()}finally{e.flushing=!1}}}async function Sb(t,e,n,r){let o=ls(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(ao(t,[n]),await V()),pg(t.id,o))try{await R(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 Tb(t,e,n,r,o=null,s=null){await Sb(t,e,r,o);let a=Ss.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=ZR(r,t.pendingHandoffHydrate.artifact||Oi),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=ob(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
166
166
 
167
- ${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}:{}});ir(t,null);try{if(await ln({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:As(n.agent)}),(ks.get(t.id)||0)!==a){!o&&r.trim()&&(no(t,[r]),await X());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,{...Ce(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 E$(t,e,i)||(await Tb(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||no(t,[r])),c&&await G({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function xb(t,{actions:e}={}){wd(t).items.push({id:Ts(),text:Bg(e),ts:Date.now(),kind:"ship-notify"}),yb(t)}async function le(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"||!Hr(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=tt(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=Hr(e);await bb(s,a,i,n),$i(t,"say");let c=await rt(s.leadBox);if(!c&&await kd(s)&&(await X(),c=await rt(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=wd(t),u=await mt(s.leadBox,{lines:_t}).catch(()=>""),f=l.items.length>0||ma(a?.agent,u);if(!f&&Ur(a?.agent,u)){let d=await an(s.leadBox).catch(()=>u);f=!!nr(a,d)}if(f){let d={id:Ts(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),yb(t),ir(s,null),await X(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await kb(s,a,c,i,n,o),await X(),{ok:!0,leadBox:s.leadBox}}var sb="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 Sb(t,{hint:e}={}){let n=tt(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=sl(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";Hm(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${sb}
167
+ ${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}:{}});lr(t,null);try{if(await un({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:Is(n.agent)}),(Ss.get(t.id)||0)!==a){!o&&r.trim()&&(ao(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 _t(t.leadBox,i,{...Me(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 $$(t,e,i)||(await Ib(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||ao(t,[r])),c&&await q({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function Ab(t,{actions:e}={}){xd(t).items.push({id:Es(),text:Ug(e),ts:Date.now(),kind:"ship-notify"}),vb(t)}async function ce(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"||!Gr(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=Gr(e);await Sb(s,a,i,n),Ci(t,"say");let c=await tt(s.leadBox);if(!c&&await Ad(s)&&(await V(),c=await tt(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=xd(t),u=await mt(s.leadBox,{lines:At}).catch(()=>""),f=l.items.length>0||ya(a?.agent,u);if(!f&&Kr(a?.agent,u)){let d=await cn(s.leadBox).catch(()=>u);f=!!or(a,d)}if(f){let d={id:Es(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),vb(t),lr(s,null),await V(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await Tb(s,a,c,i,n,o),await V(),{ok:!0,leadBox:s.leadBox}}var lb="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 _b(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";Gm(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${lb}
168
168
 
169
- What to preview: ${String(e).trim()}`:sb;try{await le(t,a,{kind:"spin-up-view"})}catch(i){throw $a(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function gd(t){let e=tt(await H(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);ks.set(t,(ks.get(t)||0)+1);let n=Ri(t),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=eo.get(t);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=no(e,r),await X()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:e.draftRev||0}}let s=y$(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,En(f$)]);let a=tt(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 f$=4e3,p$=3,m$=[300,350,400],h$=1e3,w$=12,eo=new Map,ks=new Map;function g$(t){let e=eo.get(t);return!!e&&!e.canceled}function $i(t,e){let n=eo.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function vb(t){$i(t,"track-ended"),ks.delete(t),Ri(t)}function y$(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}),eo.set(t,r),(async()=>{let s=tt(await H(),t),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await rt(s.leadBox):null,c=!1,l=!1;if(i){t:for(let y=0;y<p$&&!r.canceled;y++){await vt(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let b of m$){await En(b);let S=await mt(s.leadBox,{lines:_t}).catch(()=>"");if(!Co(a?.agent,S)){c=!0;break t}}}if(!c){l=!0;for(let y=0;y<w$&&!r.canceled&&(await En(h$),!r.canceled);y++){let b=await mt(s.leadBox,{lines:_t}).catch(()=>"");if(!Co(a?.agent,b)){c=!0;break}await vt(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=tt(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 S$(u,f).catch(()=>null),d||(p=await A$(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await k$(u).catch(()=>null):null,w=[d||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=no(u,w),await X()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await b$(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 G({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"),eo.get(t)===r&&eo.delete(t),o()}),r}async function b$(t,e,n){await I(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}async function k$(t){let e=Yu(await L(t));return e?.offerId?(await I(t,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,action:"dismiss",actions:null,reason:"interrupt"}}),e.offerId):null}var x$=[250,400,500,600];async function S$(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 x$){await En(r);let o=await mt(t.leadBox,{lines:_t}).catch(()=>""),s=rs(e.agent,o);if(s&&lu(n.text,s))return await Tb(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 v$=3e4,T$=[350,650,900];async function A$(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>v$||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!St(r.text))return null;for(let s of T$){await En(s);let a;try{a=await eu(e,{})}catch{return null}if(Hw(a,r))return null;let i=await mt(t.leadBox,{lines:_t}).catch(()=>"");if(rs(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 _$=[250,450,700],id=2;async function E$(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=id;r++){let o=!1;for(let s of _$){await En(s);let a=await mt(t.leadBox,{lines:_t}).catch(()=>""),i=Ww(e.agent,n,a,{busy:Co(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<id&&(await vt(t.leadBox,["Enter"],{kind:"submit-retry"}),G({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return G({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:id,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function Tb(t,e,n){for(let r=0;r<3;r++){await la(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await En(200);let o=await mt(t,{lines:_t}).catch(()=>""),s=rs(e,o);if(!s)return!0;n=s.length+8}return!1}function ir(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 no(t,e){let n=[],r=o=>n.some(s=>St(s).includes(St(o)));for(let o of[t.draft?.text,...e])o&&o.trim()&&!r(o)&&n.push(o);return n.length?ir(t,n.join(`
169
+ What to preview: ${String(e).trim()}`:lb;try{await ce(t,a,{kind:"spin-up-view"})}catch(i){throw Ca(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function vd(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`);Ss.set(t,(Ss.get(t)||0)+1);let n=Mi(t),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=so.get(t);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=ao(e,r),await V()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:e.draftRev||0}}let s=x$(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Rn(h$)]);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 h$=4e3,w$=3,g$=[300,350,400],y$=1e3,b$=12,so=new Map,Ss=new Map;function k$(t){let e=so.get(t);return!!e&&!e.canceled}function Ci(t,e){let n=so.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function Eb(t){Ci(t,"track-ended"),Ss.delete(t),Mi(t)}function x$(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}),so.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<w$&&!r.canceled;y++){await St(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of g$){await Rn(k);let v=await mt(s.leadBox,{lines:At}).catch(()=>"");if(!Bo(a?.agent,v)){c=!0;break t}}}if(!c){l=!0;for(let y=0;y<b$&&!r.canceled&&(await Rn(y$),!r.canceled);y++){let k=await mt(s.leadBox,{lines:At}).catch(()=>"");if(!Bo(a?.agent,k)){c=!0;break}await St(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 A$(u,f).catch(()=>null),d||(p=await I$(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await S$(u).catch(()=>null):null,w=[d||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=ao(u,w),await V()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await v$(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"),so.get(t)===r&&so.delete(t),o()}),r}async function v$(t,e,n){await R(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}async function S$(t){let e=nd(await L(t));return e?.offerId?(await R(t,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,action:"dismiss",actions:null,reason:"interrupt"}}),e.offerId):null}var T$=[250,400,500,600];async function A$(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 T$){await Rn(r);let o=await mt(t.leadBox,{lines:At}).catch(()=>""),s=as(e.agent,o);if(s&&mu(n.text,s))return await Ib(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 _$=3e4,E$=[350,650,900];async function I$(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>_$||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!xt(r.text))return null;for(let s of E$){await Rn(s);let a;try{a=await au(e,{})}catch{return null}if(Gw(a,r))return null;let i=await mt(t.leadBox,{lines:At}).catch(()=>"");if(as(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 R$=[250,450,700],fd=2;async function $$(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=fd;r++){let o=!1;for(let s of R$){await Rn(s);let a=await mt(t.leadBox,{lines:At}).catch(()=>""),i=Vw(e.agent,n,a,{busy:Bo(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<fd&&(await St(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:fd,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function Ib(t,e,n){for(let r=0;r<3;r++){await pa(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Rn(200);let o=await mt(t,{lines:At}).catch(()=>""),s=as(e,o);if(!s)return!0;n=s.length+8}return!1}function lr(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 ao(t,e){let n=[],r=o=>n.some(s=>xt(s).includes(xt(o)));for(let o of[t.draft?.text,...e])o&&o.trim()&&!r(o)&&n.push(o);return n.length?lr(t,n.join(`
170
170
 
171
- `),{source:"restore"})?.text??null:null}var I$=12e4;function R$(t,e){let n=St(e);if(!n)return!1;if(dd(t.id).some(o=>St(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>I$)break;if(!(s.type!=="said"||!s.text||St(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Ab(t,e,{rev:n=null}={}){if(typeof e!="string")throw new Error("text (string) required");let r=tt(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=Hr(e);if(typeof n=="number"&&n<(r.draftRev||0)&&o&&R$(r,o))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let a=r.draft.text;o?St(o).includes(St(a))?ir(r,o):ir(r,`${a}
171
+ `),{source:"restore"})?.text??null:null}var O$=12e4;function M$(t,e){let n=xt(e);if(!n)return!1;if(wd(t.id).some(o=>xt(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>O$)break;if(!(s.type!=="said"||!s.text||xt(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Rb(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=Gr(e);if(typeof n=="number"&&n<(r.draftRev||0)&&o&&M$(r,o))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let a=r.draft.text;o?xt(o).includes(xt(a))?lr(r,o):lr(r,`${a}
172
172
 
173
- ${o}`,{source:"restore"}):ir(r,a,{source:"restore"})}else ir(r,o);return await X(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function _b(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=tt(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await rt(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=kg(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await Oo(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await ln({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:As(o.agent)});let u=await xg(a,r.leadBox,i);return dt(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await X(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function Eb(t){for(let e of t){if(!e||!await Za(e))continue;let n=await Jw(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 us(t){let e=[];for(let n of qt(t?.workspace)){if(!n?.dir)continue;let r=await gu(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await yu(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function Ib(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await bo(e)??await fc(e));let n=await Eb([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 $$(t,e,n,r){let o=`track/${vd(e)}-${t}`,s=[];try{await F.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Ib(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await ot(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 Qa(c);await Wr(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await Oi(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await ee(i.base,i.dir).catch(()=>{});throw await F.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var Rb=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 $b(t,e){let n=await H(),r=tt(n,t);if(!r)throw new Error(`track "${t}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await Ib(typeof e=="string"?e.trim():e);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await ot(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 Qa(s);await Xw(s,o.branch)?await Yw(s,l,o.branch):await Wr(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 Oi(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Rb(o)}),await X(),f}async function Ob(t,e){let n=await H(),r=tt(n,t);if(!r)throw new Error(`track "${t}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===e||c.repo===e||c.base===e));if(!s)throw new Error(`repo "${e}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of ce.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 gu(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await yu(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 ee(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 Oi(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Rb(o)}),await X(),{removed:s.name}}async function O$(t,e,{repo:n,repos:r,cwd:o}={}){await F.mkdir(ud,{recursive:!0});let s=U.join(ud,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return $$(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await bo(n)??await fc(n));let i=await Eb([n,o,VR]);if(i){let c=`track/${vd(e)}-${t}`;try{let l=await Qa(i);await Wr(i,s,c,l),await fd(s);let u=await ot(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 Zw(s),await fd(s),{dir:s,base:null,branch:null,kind:"repo"}}var xs="# bostrat workspace",Mb=`${xs}
173
+ ${o}`,{source:"restore"}):lr(r,a,{source:"restore"})}else lr(r,o);return await V(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function $b(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=Tg(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await Po(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await un({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Is(o.agent)});let u=await Ag(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 Ob(t){for(let e of t){if(!e||!await ei(e))continue;let n=await Qw(e).catch(()=>null);if(!n)continue;if((await F.stat(U.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Wt(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 ps(t){let e=[];for(let n of Wt(t?.workspace)){if(!n?.dir)continue;let r=await vu(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Su(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function Mb(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await So(e)??await gc(e));let n=await Ob([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 C$(t,e,n,r){let o=`track/${Id(e)}-${t}`,s=[];try{await F.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Mb(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 ni(c);await Vr(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await Ni(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await te(i.base,i.dir).catch(()=>{});throw await F.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var Cb=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 Nb(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 Mb(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 ni(s);await tg(s,o.branch)?await eg(s,l,o.branch):await Vr(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 Ni(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Cb(o)}),await V(),f}async function Pb(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 ie.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 vu(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Su(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 te(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 Ni(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Cb(o)}),await V(),{removed:s.name}}async function N$(t,e,{repo:n,repos:r,cwd:o}={}){await F.mkdir(hd,{recursive:!0});let s=U.join(hd,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return C$(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await So(n)??await gc(n));let i=await Ob([n,o,XR]);if(i){let c=`track/${Id(e)}-${t}`;try{let l=await ni(i);await Vr(i,s,c,l),await gd(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 ng(s),await gd(s),{dir:s,base:null,branch:null,kind:"repo"}}var Ts="# bostrat workspace",Db=`${Ts}
174
174
 
175
175
  TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
176
176
  before substantive work \u2014 it is the shared cross-provider memory for this repo
@@ -339,9 +339,9 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
339
339
  statement, or omitted entirely renders no controls \u2014 so close EVERY completed
340
340
  implementation turn this way, every time. When the operator has already asked you
341
341
  to commit / push / open the PR, just do it; do not re-offer.
342
- `,M$=Mb,gs=Mb+Vw;async function fd(t){try{await F.writeFile(U.join(t,"AGENTS.md"),gs,{flag:"wx"})}catch{return}await P$(t,["/AGENTS.md"])}var cd=t=>typeof t=="string"&&(t===xs||t.startsWith(`${xs}
343
- `)),C$="# bostrat multi-repo workspace";function N$(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
344
- `);return`${C$}
342
+ `,P$=Db,ks=Db+Yw;async function gd(t){try{await F.writeFile(U.join(t,"AGENTS.md"),ks,{flag:"wx"})}catch{return}await j$(t,["/AGENTS.md"])}var pd=t=>typeof t=="string"&&(t===Ts||t.startsWith(`${Ts}
343
+ `)),D$="# bostrat multi-repo workspace";function B$(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
344
+ `);return`${D$}
345
345
 
346
346
  This directory is NOT a git repo. It is a bostrat Track workspace holding one
347
347
  git worktree per tagged repo, ALL on the same branch \`${e}\`:
@@ -353,25 +353,25 @@ push / open PRs) in each repo separately, always on that shared branch. Work
353
353
  only needs to land in the repos it actually touches \u2014 an untouched repo's
354
354
  branch simply stays at its fork point and never gets pushed. When asked to run
355
355
  or preview an app, run it from that app's own repo directory.
356
- `}async function Oi(t,e,n){let r=N$(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await F.writeFile(U.join(t,o),r).catch(()=>{})}var ld=gs.replace(`${xs}
357
- `,`${xs}
356
+ `}async function Ni(t,e,n){let r=B$(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await F.writeFile(U.join(t,o),r).catch(()=>{})}var md=ks.replace(`${Ts}
357
+ `,`${Ts}
358
358
 
359
359
  (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
360
360
  SCOPE: these instructions apply ONLY when your working directory is under
361
361
  ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
362
362
  When working anywhere else, ignore everything below.
363
- `);async function Cb(t){let{stdout:e}=await KR("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function P$(t,e){try{let n=await Cb(t);await F.mkdir(U.dirname(n),{recursive:!0});let r=await F.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
363
+ `);async function Bb(t){let{stdout:e}=await zR("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function j$(t,e){try{let n=await Bb(t);await F.mkdir(U.dirname(n),{recursive:!0});let r=await F.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
364
364
  `).includes(s));o.length&&await F.appendFile(n,(r.endsWith(`
365
365
  `)||!r?"":`
366
366
  `)+o.join(`
367
367
  `)+`
368
- `)}catch{}}async function D$(t,e){try{let n=await Cb(t),r=await F.readFile(n,"utf8"),o=r.split(`
368
+ `)}catch{}}async function L$(t,e){try{let n=await Bb(t),r=await F.readFile(n,"utf8"),o=r.split(`
369
369
  `).filter(s=>s!==e);o.length!==r.split(`
370
370
  `).length&&await F.writeFile(n,o.join(`
371
- `))}catch{}}async function Nb(){let t={refreshed:0,removed:0,codexGlobal:null};try{await F.mkdir(Xe,{recursive:!0}),await F.writeFile(U.join(Xe,"CLAUDE.md"),M$).catch(()=>{}),await F.writeFile(U.join(Xe,"AGENTS.md"),gs).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,ld,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):cd(o)&&o!==ld&&(await F.writeFile(r,ld).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[ud,ro]){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&&cd(c)&&c!==gs&&(await F.writeFile(i,gs).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await F.readFile(l,"utf8").catch(()=>null);u!==null&&cd(u)&&(await F.rm(l,{force:!0}).catch(()=>{}),await D$(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function B$(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 ee(n.base,n.dir).catch(()=>{});await F.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await ee(e.base,e.dir):await F.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function yd({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 Ei(o,s),i=await H(),c=Ts().slice(0,8),l=await O$(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(w=>w.name))),f=`${t.trim()} lead`;u.has(Oc(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await Bn(f,l.dir,{agentCmd:a,agent:o});wa({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:[qb(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:w,baseRef:y,repo:b})=>({name:m,base:w,baseRef:y,repo:b}))}:{}}}),i.tracks.push(p),await X(),Ye(p)}var j$=new Set(["claude","codex","cursor"]);async function Pb({provider:t="claude",fresh:e=!1}={}){if(!j$.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 Ye(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await so(r.id).catch(()=>{})}let o=(await Ei(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Ts().slice(0,8),a=U.join(Xe,"setup",s);await F.mkdir(a,{recursive:!0});let i=await Bn(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&wa({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:[qb(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 X(),Ye(c)}async function Db(t,e,n){await Jm(t).catch(()=>{});let r=Ss.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Ss.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of ao(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Ad(a).catch(()=>{}),ce.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 pe(a.name).catch(()=>{})}async function Bb(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 $m(s),vb(t),await Db(t,s,"track deleted"),n?await ah(t).catch(()=>{}):await sh(t).catch(()=>{}),await vg(),await B$(s),e&&await Om(t).catch(()=>{}),e?await Jy(t).catch(()=>{}):await zy(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 X(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}var vi=new Set;async function so(t){let e=await H(),n=tt(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);return n.archivedAt=Date.now(),vb(t),dt(n,"archived",{boxes:n.boxes.length}),await X(),n.kind==="setup"?(await jb(t),Ye(n)):(vi.add(t),F$(t).catch(()=>{}).finally(()=>vi.delete(t)),{...await Ye(n),archiving:!0})}var L$=9e4;async function F$(t){try{await U$(t)}catch{}await jb(t).catch(()=>{})}async function jb(t){let e=await H(),n=tt(e,t);if(!(!n||!n.archivedAt)){await Db(t,n,"track archived");for(let r of n.boxes)cr.has(r.state)||(r.state="stopped");await X()}}async function U$(t){let e=await H(),n=tt(e,t);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await rt(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await em(n.leadBox,i.cwd,{timeoutMs:L$})),s="lead-cli"}catch{}if(r||(r=Pl(n,await L(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await I(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await X()}async function Lb(){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=Pl(o,await L(o).catch(()=>[]));if(!s){r++;continue}o.beat={text:s,source:"derived",at:Date.now()},await I(o,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",payload:{text:s,source:"derived",backfill:!0}}).catch(()=>{}),e++}return await X(),{recorded:e,skipped:n,empty:r}}async function vn(t,e){let n=await Bt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Fb(n);if(r.length){for(let o of r)await Jn(o.repo,o.number,!0).catch(()=>{});return!1}try{return await so(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Fb(t){return(await ve().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 Tn(t,e){return bd(t,e,"merged")}async function bd(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 Fb(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Gh(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await le(r.trackId,H$(a),{kind:"merge-notify"}),!0}catch{return!1}}var ab={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function H$(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 ${ab[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 ${ab[i.outcome]??i.outcome}`).join(`
371
+ `))}catch{}}async function jb(){let t={refreshed:0,removed:0,codexGlobal:null};try{await F.mkdir(Xe,{recursive:!0}),await F.writeFile(U.join(Xe,"CLAUDE.md"),P$).catch(()=>{}),await F.writeFile(U.join(Xe,"AGENTS.md"),ks).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,md,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):pd(o)&&o!==md&&(await F.writeFile(r,md).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[hd,io]){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&&pd(c)&&c!==ks&&(await F.writeFile(i,ks).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await F.readFile(l,"utf8").catch(()=>null);u!==null&&pd(u)&&(await F.rm(l,{force:!0}).catch(()=>{}),await L$(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function F$(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of Wt(e))n.base&&n.dir&&await te(n.base,n.dir).catch(()=>{});await F.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await te(e.base,e.dir):await F.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Sd({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 $i(o,s),i=await H(),c=Es().slice(0,8),l=await N$(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(w=>w.name))),f=`${t.trim()} lead`;u.has(Dc(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await Fn(f,l.dir,{agentCmd:a,agent:o});ka({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:[zb(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:w,baseRef:y,repo:k})=>({name:m,base:w,baseRef:y,repo:k}))}:{}}}),i.tracks.push(p),await V(),Ye(p)}var U$=new Set(["claude","codex","cursor"]);async function Lb({provider:t="claude",fresh:e=!1}={}){if(!U$.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 Ye(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await lo(r.id).catch(()=>{})}let o=(await $i(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Es().slice(0,8),a=U.join(Xe,"setup",s);await F.mkdir(a,{recursive:!0});let i=await Fn(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&ka({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:[zb(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(),Ye(c)}async function Fb(t,e,n){await Qm(t).catch(()=>{});let r=As.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),As.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of uo(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await $d(a).catch(()=>{}),ie.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 pe(a.name).catch(()=>{})}async function Ub(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 Nm(s),Eb(t),await Fb(t,s,"track deleted"),n?await uh(t).catch(()=>{}):await lh(t).catch(()=>{}),await Eg(),await F$(s),e&&await Pm(t).catch(()=>{}),e?await Qy(t).catch(()=>{}):await Zy(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 _i=new Set;async function lo(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(),Eb(t),dt(n,"archived",{boxes:n.boxes.length}),await V(),n.kind==="setup"?(await Hb(t),Ye(n)):(_i.add(t),W$(t).catch(()=>{}).finally(()=>_i.delete(t)),{...await Ye(n),archiving:!0})}var H$=9e4;async function W$(t){try{await q$(t)}catch{}await Hb(t).catch(()=>{})}async function Hb(t){let e=await H(),n=Z(e,t);if(!(!n||!n.archivedAt)){await Fb(t,n,"track archived");for(let r of n.boxes)ur.has(r.state)||(r.state="stopped");await V()}}async function q$(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 om(n.leadBox,i.cwd,{timeoutMs:H$})),s="lead-cli"}catch{}if(r||(r=Fl(n,await L(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await R(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 Wb(){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=Fl(o,await L(o).catch(()=>[]));if(!s){r++;continue}o.beat={text:s,source:"derived",at:Date.now()},await R(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 An(t,e){let n=await Pt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await qb(n);if(r.length){for(let o of r)await Yn(o.repo,o.number,!0).catch(()=>{});return!1}try{return await lo(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function qb(t){return(await ve().catch(()=>[])).filter(n=>n.trackId===t.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===t.repo&&n.number===t.number))}async function _n(t,e){return Td(t,e,"merged")}async function Td(t,e,n="merged"){let r=await Pt(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 qb(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Xh(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await ce(r.trackId,K$(a),{kind:"merge-notify"}),!0}catch{return!1}}var ub={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function K$(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 ${ub[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 ${ub[i.outcome]??i.outcome}`).join(`
372
372
  `),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}
373
373
  ${s}
374
- ${a}`}async function kd(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||cr.has(e.state)||await rt(e.name))return!1;let n=lb(e.agent,e.sessionId),r=await ia(n);if(r===gr)return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===yr&&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 Bn(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=go(o.hookToken),wa({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 Ub(t){let e=await H(),n=tt(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,vi.delete(t),dt(n,"unarchived",{}),await kd(n),await X(),Ye(n)}async function Hb(){let t=await H(),e=new Set((await Oe()).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 pe(a.name).catch(()=>{}),dt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!cr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await X(),r}async function Wb(t){let e=await H(),n=tt(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(cr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await rt(n.leadBox)){let a=await mt(n.leadBox,{lines:_t}).catch(()=>"");if(ma(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await pe(n.leadBox),r.state="stopped"}let s=await kd(n);if(await X(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Ye(n)}var qb=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:go(t.hookToken),createdAt:Date.now()});function Kb(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 Ss=new Map,_s=t=>Ss.get(t)||(Ee?tt(Ee,t)?.handoff:null)||null;async function Ti(t,e){let n=await H(),r=tt(n,t);r&&(r.handoff={...r.handoff||{},...e},await X())}function W$(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 Gb(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=W$(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await rt(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=Kb(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Ii,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 G({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await G({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await pe(i).catch(()=>{}),a&&await pe(a).catch(()=>{})}return e&&await X(),e}async function xd(t,e,n=!0,r=null){let o=Ss.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=tt(await H(),t);if(!s)throw new Error(`track "${t}" not found`);$i(t,"handoff");let a=await Ei(e,n),i=as(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 rt(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:ub(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Ss.set(t,l),await Ti(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),q$(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 Ti(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),G({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),G({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function q$(t,e,n,r,o){let s=async(E,O={})=>{t.step=E,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${E}`),await Ti(t.trackId,{step:E,toBox:t.toBox}),await G({flow:"lead-handoff",phase:E,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:O.sessionName,...O})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await H(),i=tt(a,t.trackId)?.boxes.find(E=>E.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;await s("checkpoint",{sessionName:t.fromBox}),await cm(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await Bl(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await ln({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:As(o)});let d=await mt(t.fromBox,{lines:_t}).catch(()=>"");if(!ha(o,d)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let E=lb(c,l,{skipPermissions:t.skipPermissions});await Bl(t.fromBox,{agentCmd:E,agent:c,resumeSessionId:l,pinned:u});let O=await H(),nt=tt(O,t.trackId);if(nt){let R=nt.boxes.find(C=>C.name===t.fromBox);R&&(R.state="live"),dt(nt,"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 X()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let p=await H(),m=tt(p,t.trackId);if(!m)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let w=m.boxes.find(E=>E.name===t.fromBox);if(!w)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let y=Date.now(),b=Kb(w),S=b[b.length-1];S.endedAt=y,t.reason&&(S.endReason=t.reason),b.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:y,endedAt:null}),w.agent=o,w.sessionId=f.sessionId,w.pinned=f.pinned,w.state="live",w.role="lead",m.pendingHandoffHydrate={artifact:Ii,at:y},dt(m,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:b.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt}),await X(),await s("done",{sessionName:t.fromBox}),await Ti(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var K$=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,G$=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ro=U.join(Xe,"worktrees"),Qr=[ys,Si],ce=new Map;var ao=t=>{let e=new Map([...ce.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=Ee?tt(Ee,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)},Sd=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 V$(t,e,n){let r=await H(),o=tt(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await X())}var oo=t=>V$(t.trackId,t.id,Sd(t));async function to(t,e){t.step=e,await oo(t)}function z$(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 J$(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function X$(t,e,n){if(!J$(n)||ce.has(e)||!await rt(n.box).catch(()=>null))return!1;try{await F.access(n.dir)}catch{return!1}let o=await rt(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return ce.set(e,s),Yb(s,o).catch(a=>Jb(s,a)),!0}async function Vb(){let t=await H(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(ce.has(r))continue;if(await X$(n,r,o)){e=!0,dt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await G({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=z$(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!cr.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 G({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await ib(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||cr.has(r.state)||ce.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?U.join(ro,`${n.id}-${r.subtaskId}`):null;await G({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await ib(n,r.name,o)}}return e=await Y$(t)||e,e&&await X(),e}async function ib(t,e,n){if(e&&await pe(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of qt(t.workspace)){let s=U.join(n,o.name);await ee(o.base,s).then(()=>!0,()=>!1)||await ee(o.dir,s).catch(()=>{})}await F.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await rt(t.leadBox).catch(()=>null);r?.cwd&&await ee(r.cwd,n).catch(()=>{})}async function Y$(t){let e;try{e=await F.readdir(ro)}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 ce.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(ro,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of qt(a.workspace))await ee(i.base,U.join(s,i.name)).catch(()=>{});await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await rt(a.leadBox).catch(()=>null):null;i?.cwd?await ee(i.cwd,s).catch(()=>{}):await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await G({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var vd=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",vs=class extends Error{};async function zb(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Hr(n))throw new Error("task (string) required");let s=await Ei(e,r),a=as(e);if(a){let p=new Error(`can't delegate to ${e} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=tt(await H(),t);if(!i)throw new Error(`track "${t}" not found`);let c=await rt(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 Za(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 Za(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Ts().slice(0,8),d={id:f,trackId:t,trackName:i.name,agent:e,task:Hr(n),skipPermissions:r,reason:ub(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${vd(i.name)}-${f}`,base:null,repos:u,dir:U.join(ro,`${t}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return ce.set(f,d),await oo(d),Z$(d,c,s).catch(p=>Jb(d,p)),d}async function Jb(t,e){t.failedStep=t.step,t.error=e instanceof vs?"canceled":e?.message||String(e),t.step=e instanceof vs?"canceled":"error",t.finishedAt=Date.now(),await Ad(t).catch(()=>{}),await oo(t).catch(()=>{}),await nO(t).catch(()=>{})}function Td(t){let e=ce.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 Xb(t){let e=ce.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 Z$(t,e,n){if(await to(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await ss(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await oo(t)}else t.base=await ss(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await to(t,"spawn"),await F.mkdir(ro,{recursive:!0}),t.repos){await F.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Wr(a.leadDir,U.join(t.dir,a.name),t.branch,a.base);let s=qt((tt(await H(),t.trackId)||{}).workspace);await Oi(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 Wr(e.cwd,t.dir,t.branch,t.base),await fd(t.dir);await Q$(t,e);let r=t.trackName.slice(0,38).trim(),o=await Bn(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await tO(t,o),await db(t.trackId,o.name,YR(t.task,!!t.repos),{kind:"subtask"}),await Yb(t,e)}async function Yb(t,e){await to(t,"working");let n=U.join(t.dir,Si);if(await oO(n,t),t.canceled)throw new vs;if(await to(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 wu(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await ku(c.leadDir,c.base,t.branch,Qr),c.files=await xu(c.leadDir,c.base,t.branch,Qr);let u=await Su(c.leadDir,c.base,t.branch,Qr);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 wu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await ku(e.cwd,t.base,t.branch,Qr),t.files=await xu(e.cwd,t.base,t.branch,Qr);let o=await Su(e.cwd,t.base,t.branch,Qr);t.applied=o.applied,t.conflicts=o.conflicts}await oo(t);let r=await rO(t,e,n);await to(t,"resume"),await db(t.trackId,t.leadBox,ZR(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await to(t,"reap"),await Ad(t),t.step="done",t.finishedAt=Date.now(),await eO(t),await oo(t)}async function Q$(t,e){let n=U.join(t.dir,U.dirname(ys));await F.mkdir(n,{recursive:!0});let r="";try{r=await gp(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}
374
+ ${a}`}async function Ad(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||ur.has(e.state)||await tt(e.name))return!1;let n=pb(e.agent,e.sessionId),r=await da(n);if(r===kr)return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===xr&&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 Fn(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=xo(o.hookToken),ka({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 Kb(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,_i.delete(t),dt(n,"unarchived",{}),await Ad(n),await V(),Ye(n)}async function Gb(){let t=await H(),e=new Set((await $e()).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 pe(a.name).catch(()=>{}),dt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!ur.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await V(),r}async function Vb(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(ur.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:At}).catch(()=>"");if(ya(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await pe(n.leadBox),r.state="stopped"}let s=await Ad(n);if(await V(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Ye(n)}var zb=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:xo(t.hookToken),createdAt:Date.now()});function Jb(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 As=new Map,Rs=t=>As.get(t)||(_e?Z(_e,t)?.handoff:null)||null;async function Ei(t,e){let n=await H(),r=Z(n,t);r&&(r.handoff={...r.handoff||{},...e},await V())}function G$(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 Xb(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=G$(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=Jb(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Oi,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 pe(i).catch(()=>{}),a&&await pe(a).catch(()=>{})}return e&&await V(),e}async function _d(t,e,n=!0,r=null){let o=As.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`);Ci(t,"handoff");let a=await $i(e,n),i=ls(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:mb(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return As.set(t,l),await Ei(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),V$(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 Ei(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 V$(t,e,n,r,o){let s=async(E,$={})=>{t.step=E,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${E}`),await Ei(t.trackId,{step:E,toBox:t.toBox}),await q({flow:"lead-handoff",phase:E,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:$.sessionName,...$})};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(E=>E.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;await s("checkpoint",{sessionName:t.fromBox}),await dm(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await Hl(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await un({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:Is(o)});let d=await mt(t.fromBox,{lines:At}).catch(()=>"");if(!ba(o,d)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let E=pb(c,l,{skipPermissions:t.skipPermissions});await Hl(t.fromBox,{agentCmd:E,agent:c,resumeSessionId:l,pinned:u});let $=await H(),z=Z($,t.trackId);if(z){let O=z.boxes.find(P=>P.name===t.fromBox);O&&(O.state="live"),dt(z,"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 p=await H(),m=Z(p,t.trackId);if(!m)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let w=m.boxes.find(E=>E.name===t.fromBox);if(!w)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let y=Date.now(),k=Jb(w),v=k[k.length-1];v.endedAt=y,t.reason&&(v.endReason=t.reason),k.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:y,endedAt:null}),w.agent=o,w.sessionId=f.sessionId,w.pinned=f.pinned,w.state="live",w.role="lead",m.pendingHandoffHydrate={artifact:Oi,at:y},dt(m,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:k.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt}),await V(),await s("done",{sessionName:t.fromBox}),await Ei(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var z$=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,J$=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,io=U.join(Xe,"worktrees"),ro=[xs,Ai],ie=new Map;var uo=t=>{let e=new Map([...ie.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)},Ed=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 X$(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 co=t=>X$(t.trackId,t.id,Ed(t));async function oo(t,e){t.step=e,await co(t)}function Y$(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 Z$(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function Q$(t,e,n){if(!Z$(n)||ie.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 ie.set(e,s),ek(s,o).catch(a=>Qb(s,a)),!0}async function Yb(){let t=await H(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(ie.has(r))continue;if(await Q$(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=Y$(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!ur.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 db(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||ur.has(r.state)||ie.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?U.join(io,`${n.id}-${r.subtaskId}`):null;await q({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await db(n,r.name,o)}}return e=await tO(t)||e,e&&await V(),e}async function db(t,e,n){if(e&&await pe(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of Wt(t.workspace)){let s=U.join(n,o.name);await te(o.base,s).then(()=>!0,()=>!1)||await te(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 te(r.cwd,n).catch(()=>{})}async function tO(t){let e;try{e=await F.readdir(io)}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 ie.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(io,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Wt(a.workspace))await te(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 te(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 Id=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",_s=class extends Error{};async function Zb(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Gr(n))throw new Error("task (string) required");let s=await $i(e,r),a=ls(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=Wt(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 ei(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 ei(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Es().slice(0,8),d={id:f,trackId:t,trackName:i.name,agent:e,task:Gr(n),skipPermissions:r,reason:mb(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Id(i.name)}-${f}`,base:null,repos:u,dir:U.join(io,`${t}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return ie.set(f,d),await co(d),eO(d,c,s).catch(p=>Qb(d,p)),d}async function Qb(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 $d(t).catch(()=>{}),await co(t).catch(()=>{}),await sO(t).catch(()=>{})}function Rd(t){let e=ie.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 tk(t){let e=ie.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 eO(t,e,n){if(await oo(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await cs(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await co(t)}else t.base=await cs(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await oo(t,"spawn"),await F.mkdir(io,{recursive:!0}),t.repos){await F.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Vr(a.leadDir,U.join(t.dir,a.name),t.branch,a.base);let s=Wt((Z(await H(),t.trackId)||{}).workspace);await Ni(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 Vr(e.cwd,t.dir,t.branch,t.base),await gd(t.dir);await nO(t,e);let r=t.trackName.slice(0,38).trim(),o=await Fn(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await rO(t,o),await hb(t.trackId,o.name,t$(t.task,!!t.repos),{kind:"subtask"}),await ek(t,e)}async function ek(t,e){await oo(t,"working");let n=U.join(t.dir,Ai);if(await iO(n,t),t.canceled)throw new _s;if(await oo(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 xu(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await Au(c.leadDir,c.base,t.branch,ro),c.files=await _u(c.leadDir,c.base,t.branch,ro);let u=await Eu(c.leadDir,c.base,t.branch,ro);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 xu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Au(e.cwd,t.base,t.branch,ro),t.files=await _u(e.cwd,t.base,t.branch,ro);let o=await Eu(e.cwd,t.base,t.branch,ro);t.applied=o.applied,t.conflicts=o.conflicts}await co(t);let r=await aO(t,e,n);await oo(t,"resume"),await hb(t.trackId,t.leadBox,e$(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await oo(t,"reap"),await $d(t),t.step="done",t.finishedAt=Date.now(),await oO(t),await co(t)}async function nO(t,e){let n=U.join(t.dir,U.dirname(xs));await F.mkdir(n,{recursive:!0});let r="";try{r=await kp(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}
375
375
 
376
376
  **Delegated task:** ${t.task}
377
377
 
@@ -382,50 +382,50 @@ ${a}`}async function kd(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name
382
382
  ## Conversation so far (lead \u2192 you)
383
383
 
384
384
  ${r}
385
- `;await F.writeFile(U.join(t.dir,ys),o)}async function tO(t,e){let n=await H(),r=tt(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:go(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]||{},...Sd(t)},await X())}async function eO(t){let e=await H(),n=tt(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 X()}async function nO(t){let e=await H(),n=tt(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),dt(n,r?"subtask-canceled":"subtask-failed",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,failedStep:t.failedStep||t.step,error:t.error,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[t.id]={...n.subtasks[t.id]||{},...Sd(t)},await X()}async function rO(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 Ad(t){if(t.box&&await pe(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await ee(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 rt(t.leadBox).catch(()=>null))?.cwd;n&&await ee(n,t.dir).catch(()=>{})}}async function oO(t,e){let n=(e.startedAt||Date.now())+K$;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 En(G$)}}function En(t){return new Promise(e=>setTimeout(e,t))}import Es from"node:fs/promises";import Ni from"node:path";import lO from"node:os";import sO from"ws";var Zb=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Mi(t,e,n={}){let r=await fetch(`${Zb()}/${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 _d=(t,e)=>Mi("chat.postMessage",t,e),io=(t,e)=>Mi("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 aO(t,e,n={}){let r=await fetch(`${Zb()}/${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 Qb(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await aO("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 Mi("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var iO=2200;function tk({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 Mi("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new sO(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=w=>{try{o.send(JSON.stringify(w?{envelope_id:f.envelope_id,payload:w}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},iO);Promise.resolve().then(()=>e(f)).then(w=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var rk=()=>process.env.BOSTRAT_STATE_DIR||Ni.join(process.env.HOME||lO.tmpdir(),".bostrat"),Od=()=>Ni.join(rk(),"slack.json"),Ci=null,Ed=null,Id=Promise.resolve();async function Cd(){return Ci||(Ed??=Es.readFile(Od(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>Ci=t).finally(()=>Ed=null),Ed)}async function ek(){let t=JSON.stringify({threads:Ci},null,2),e=`${Od()}.${process.pid}.tmp`;await Es.mkdir(rk(),{recursive:!0}),await Es.writeFile(e,t),await Es.rename(e,Od())}async function Ze(){Id=Id.then(ek,ek),await Id.catch(()=>{})}async function Nd(t){return(await Cd())[t]??null}async function uO(t,e){let n=await Cd();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var Q=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function Is(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function dO(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Is(n.replace(/\n+/g," "),e)}function ok(t){let e=`*${Q(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${Q(t.repoLabel)} \xB7 \`${Q(t.branch||"?")}\` \xB7 lead *${Q(t.lead)}*`,r=t.kickoff?`> ${Q(Is(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
386
- ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Fa(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Md(t,e){await io(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:ok(e)})}async function ur(t,e,n,r=void 0){return _d(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var nk=new Set;function lr(t){nk.add(t),t.catch(()=>{}).finally(()=>nk.delete(t))}var pt=t=>({response_type:"ephemeral",text:t}),fO="*/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._",pO=["claude","cursor","codex"];function Rd(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 $d=(t,e)=>pt(`No live Track matches \u201C${Q(e)}\u201D. Live: ${t.map(n=>`\`${Q(n.name)}\``).join(" \xB7 ")||"none"}`);async function sk(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?Ni.basename(c.base):"fresh workspace"),lead:"claude"},u=await _d(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:ok(l),unfurl_links:!1});l.ts=u.ts,(await Cd())[i.id]=l,await Ze(),await Kt(o,{...pt(`\u2713 *${Q(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 mO(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Nd(n.id);s&&(s.archivedAt=Date.now(),await Ze(),await Md(e,s).catch(()=>{})),await Kt(r,{...pt(`\u2713 *${Q(n.name)}* archived.`),replace_original:!0})}catch(s){await Kt(r,{...pt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function hO(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Kt(o,{...pt(`\u2713 Handing *${Q(n.name)}* to ${Q(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 wO(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 *${Q(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await Kt(r,{...pt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function gO(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Is(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:t}),text:{type:"mrkdwn",text:`Repo for *${Q(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function yO(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?Ni.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${Q(s.name)}* \u2014 ${Q(i)}${a.branch?` \`${Q(a.branch)}\``:""} \xB7 lead ${Q(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
385
+ `;await F.writeFile(U.join(t.dir,xs),o)}async function rO(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:xo(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]||{},...Ed(t)},await V())}async function oO(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 sO(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]||{},...Ed(t)},await V()}async function aO(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 $d(t){if(t.box&&await pe(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await te(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 te(n,t.dir).catch(()=>{})}}async function iO(t,e){let n=(e.startedAt||Date.now())+z$;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 Rn(J$)}}function Rn(t){return new Promise(e=>setTimeout(e,t))}import $s from"node:fs/promises";import Bi from"node:path";import fO from"node:os";import cO from"ws";var nk=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Pi(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 Od=(t,e)=>Pi("chat.postMessage",t,e),fo=(t,e)=>Pi("chat.update",t,e);async function qt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function lO(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 rk(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await lO("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 Pi("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var uO=2200;function ok({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 Pi("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new cO(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=w=>{try{o.send(JSON.stringify(w?{envelope_id:f.envelope_id,payload:w}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},uO);Promise.resolve().then(()=>e(f)).then(w=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var ik=()=>process.env.BOSTRAT_STATE_DIR||Bi.join(process.env.HOME||fO.tmpdir(),".bostrat"),Dd=()=>Bi.join(ik(),"slack.json"),Di=null,Md=null,Cd=Promise.resolve();async function jd(){return Di||(Md??=$s.readFile(Dd(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>Di=t).finally(()=>Md=null),Md)}async function sk(){let t=JSON.stringify({threads:Di},null,2),e=`${Dd()}.${process.pid}.tmp`;await $s.mkdir(ik(),{recursive:!0}),await $s.writeFile(e,t),await $s.rename(e,Dd())}async function Ze(){Cd=Cd.then(sk,sk),await Cd.catch(()=>{})}async function Ld(t){return(await jd())[t]??null}async function pO(t,e){let n=await jd();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 Os(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function mO(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Os(n.replace(/\n+/g," "),e)}function ck(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(Os(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
386
+ ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Wa(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Bd(t,e){await fo(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:ck(e)})}async function fr(t,e,n,r=void 0){return Od(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var ak=new Set;function dr(t){ak.add(t),t.catch(()=>{}).finally(()=>ak.delete(t))}var pt=t=>({response_type:"ephemeral",text:t}),hO="*/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._",wO=["claude","cursor","codex"];function Nd(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 Pd=(t,e)=>pt(`No live Track matches \u201C${Y(e)}\u201D. Live: ${t.map(n=>`\`${Y(n.name)}\``).join(" \xB7 ")||"none"}`);async function lk(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 qt(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 qt(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?Bi.basename(c.base):"fresh workspace"),lead:"claude"},u=await Od(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:ck(l),unfurl_links:!1});l.ts=u.ts,(await jd())[i.id]=l,await Ze(),await qt(o,{...pt(`\u2713 *${Y(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await qt(o,{...pt(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function gO(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Ld(n.id);s&&(s.archivedAt=Date.now(),await Ze(),await Bd(e,s).catch(()=>{})),await qt(r,{...pt(`\u2713 *${Y(n.name)}* archived.`),replace_original:!0})}catch(s){await qt(r,{...pt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function yO(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await qt(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 qt(o,{...pt(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function bO(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await qt(r,{...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 qt(r,{...pt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function kO(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Os(`${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 xO(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?Bi.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(`
387
387
  `)+(o?`
388
- _${o} archived_`:"")}async function bO(t,e){let n=await Gn();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?(lr(sk(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),pt(`Starting *${Q(u)}* on ${Q(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:gO(u,i)}}case"archive":{if(!a.length)return pt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Rd(await r.listTracks(),a.join(" "));return i?(lr(mO(t,n,{track:i,responseUrl:e.response_url})),pt(`Archiving *${Q(i.name)}*\u2026`)):$d(c,a.join(" "))}case"handoff":{let i=r.knownAgents??pO,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}=Rd(await r.listTracks(),l);return u?(lr(hO(t,n,{track:u,agent:c,responseUrl:e.response_url})),pt(`Handing *${Q(u.name)}* to ${Q(c)}\u2026`)):$d(f,l)}case"stop":{if(!a.length)return pt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Rd(await r.listTracks(),a.join(" "));return i?(lr(wO(t,n,{track:i,responseUrl:e.response_url})),pt(`Stopping *${Q(i.name)}*\u2026`)):$d(c,a.join(" "))}case"list":return pt(await yO(r));default:return pt(fO)}}async function kO(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Nd(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 Ze(),await io(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(l||"question")}~
389
- \u2713 *${Q(a)}*`}}]}).catch(()=>{})}}catch(c){await Kt(n.response_url,pt(`Couldn't answer: ${c?.message||c}`))}}async function xO(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,ai(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 SO(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await Gn();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 *${Q(s)}*\u2026`),replace_original:!0}),lr(sk(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"?lr(kO(t,r,e,o)):n.action_id==="bostrat-ship"&&lr(xO(t,r,e,o))}async function vO(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 uO(e.channel,e.thread_ts);if(!n)return;let r=await Gn();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await ur(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function TO(t){return async e=>{if(e.type==="slash_commands")return bO(t,e.payload);if(e.type==="interactive"){await SO(t,e.payload);return}if(e.type==="events_api"){await vO(t,e.payload?.event);return}}}var AO=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 _O(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${Q(e.question||"The lead needs an answer")}`}}],r=(e.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:Is(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 EO(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await io(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await ur(t,e,r.question||"The lead needs an answer",_O(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Ze()}async function IO(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 Ze(),await io(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(s||"question")}~
390
- \u2713 _answered_`}}]})}async function RO(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 ur(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Q(Is(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await Ze()}async function $O(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await Ze();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await io(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function OO(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??MO)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Qb(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${Q(o.name)}*${a}${o.group?` (group \`${Q(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${Q(a.name)}*`).join(", ");await ur(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${Q(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function MO(t,e){let n=await dn(t);if(!n)return null;let r=Ne(n,e);if(!r)return null;let o=await Es.readFile(Jt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function CO(t,e){if(!AO.has(e.type))return;let n=await Nd(e.trackId);if(!n)return;let r=await Gn();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 Ze(),await Md(r,n);return;case"turn.agent":{if(!o.text)return;let s=dO(o.text);if(!s)return;await ur(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Q(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Fa(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return EO(r,n,e);case"prompt.answered":return IO(r,n,e);case"pr.offered":return RO(r,n,e);case"pr.offer.answered":return $O(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 ur(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 Ze(),await Md(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await ur(r,n,`Live preview ${o.name}: ${Ha(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Q(o.name)}*: ${Ha(o.slug)}
391
- _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return OO(r,n,e,t.deps)}}function ak(t){let e={deps:t};Ut.on("event",s=>{CO(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Gn()||(r&&clearInterval(r),r=null,n=tk({getToken:async()=>(await Gn())?.appToken,onEnvelope:TO(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import NO from"node:fs/promises";function PO(t){let e=new Map;for(let n of String(t||"").split(`
392
- `)){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 DO(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}function BO(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Me(e.cwd,e.sessionId):e?.cwd?Pc(e.cwd):null}async function ik(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await I(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":await i({type:"turn.user",actor:"user",payload:{promptHead:String(t.prompt??t.user_prompt??"").slice(0,160)||null}});break;case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:{tool:t.tool_name??null}});break;case"Stop":await jO(r,o,t,i);break;default:break}return a}async function jO(t,e,n,r){let o=[],s=BO(n,e);if(s)try{o=PO(await NO.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=DO(await L(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:me(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 UO from"node:fs";import Pd from"node:fs/promises";import HO from"node:http";import co from"node:path";import Di from"node:os";import{createRequire as WO}from"node:module";import Dd from"ws";import LO from"node:path";import FO from"node:os";var Rs=process.env.BOSTRAT_STATE_DIR||LO.join(process.env.HOME||FO.tmpdir(),".bostrat");function ck(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 lk(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 Pi=(()=>{try{return WO(import.meta.url)("../package.json").version||null}catch{return null}})(),qO=process.env.BOSTRAT_STATE_DIR||co.join(process.env.HOME||Di.tmpdir(),".bostrat");function KO(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 GO({home:t=process.env.HOME||Di.homedir(),env:e=process.env}={}){let n=r=>{try{return UO.statSync(r),!0}catch{return!1}};return{claude:n(co.join(t,".claude",".credentials.json")),codex:n(co.join(e.CODEX_HOME||co.join(t,".codex"),"auth.json"))}}var Rt=1,uk=1e3,VO=3e4,zO=6e4,JO=1500,XO=500,dk=100,YO=2e3,ZO=25e3,QO=6e4;function fk({url:t,token:e,nodeId:n=Di.hostname(),name:r=Di.hostname(),listTracks:o,detectAgents:s=null,sampleMetrics:a=null,metricsMs:i=QO,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=lk(t);let u=c.replace(/^http/,"ws"),f=new Map,d=new Map,p=co.join(qO,"plane-link","cursor.json"),m={},w=new Map,y=null,b=!1,S=!1,E=uk,O=null,nt=null,R=null,C=null,v=null,D=!1;async function B(){try{m=JSON.parse(await Pd.readFile(p,"utf8"))}catch{m={}}}function lt(){D=!0,!R&&(R=setTimeout(async()=>{if(R=null,!!D){D=!1;try{await Pd.mkdir(co.dirname(p),{recursive:!0}),await Pd.writeFile(p,JSON.stringify(m))}catch(K){l.error("[plane-link] cursor flush failed:",K?.message||K)}}},YO),R.unref?.())}function q(K){return!y||y.readyState!==Dd.OPEN?!1:(y.send(JSON.stringify(K)),!0)}function T(K){for(let{trackId:Y,event:$}of K)w.set($.id,{trackId:Y,seq:$.seq});return q({v:Rt,ch:"events",type:"append",events:K})}async function g(){if(!a)return;let K;try{K=await a()}catch(Y){l.error("[plane-link] metrics sample failed:",Y?.message||Y);return}K&&q({v:Rt,ch:"ctl",type:"metrics",metrics:K})}function h(){a&&(v&&clearInterval(v),g(),v=setInterval(g,i),v.unref?.())}async function A(){let K;try{K=await o()}catch(Y){l.error("[plane-link] backfill listTracks failed:",Y?.message||Y);return}q({v:Rt,ch:"ctl",type:"tracks",ids:K.map(Y=>Y.id).filter(Y=>typeof Y=="string")});for(let Y of K){if(!b)return;let $;try{$=await L(Y)}catch{continue}let z=m[Y.id]??0,xt=$.filter(ht=>ht.seq>z&&typeof ht.id=="string");for(let ht=0;ht<xt.length;ht+=dk){let k=xt.slice(ht,ht+dk).map(P=>({trackId:Y.id,event:P}));if(!T(k))return}}}function _(K){!b||!K?.trackId||typeof K.id!="string"||T([{trackId:K.trackId,event:K}])}let J=new Map;async function W(K){let{reqId:Y,method:$,path:z,headers:xt={},body:ht}=K,k=Object.fromEntries(Object.entries(xt).filter(([,$t])=>$t!=null)),P=new AbortController;J.set(Y,P);try{if(K.host){await it(K,k,P);return}let $t=await fetch(c+z,{method:$,headers:k,body:ht?Buffer.from(ht,"base64"):void 0,signal:P.signal}),Qt=Object.fromEntries($t.headers);for(let Tt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Qt[Tt];if(Qt["content-type"]||(Qt["content-type"]="application/octet-stream"),q({v:Rt,ch:"control",type:"http_head",reqId:Y,status:$t.status,headers:Qt}),$t.body){for await(let Tt of $t.body)if(!q({v:Rt,ch:"control",type:"http_data",reqId:Y,data:Buffer.from(Tt).toString("base64")}))break}q({v:Rt,ch:"control",type:"http_end",reqId:Y,error:null})}finally{J.delete(Y)}}function it(K,Y,$){let{reqId:z,method:xt,path:ht,body:k}=K,P=new URL(c);return new Promise($t=>{let Qt=HO.request({host:P.hostname,port:P.port,method:xt,path:ht,headers:{...Y,host:K.host},signal:$.signal},Tt=>{let{connection:cf,"keep-alive":KN,"transfer-encoding":GN,upgrade:VN,...Z0}=Tt.headers;q({v:Rt,ch:"control",type:"http_head",reqId:z,status:Tt.statusCode||502,headers:Z0}),Tt.on("data",Q0=>{q({v:Rt,ch:"control",type:"http_data",reqId:z,data:Buffer.from(Q0).toString("base64")})||Tt.destroy()}),Tt.on("end",()=>{q({v:Rt,ch:"control",type:"http_end",reqId:z,error:null}),$t()}),Tt.on("error",()=>{q({v:Rt,ch:"control",type:"http_end",reqId:z,error:"view upstream error"}),$t()})});Qt.on("error",Tt=>{q({v:Rt,ch:"control",type:"http_end",reqId:z,error:Tt?.message||"view request failed"}),$t()}),k&&Qt.write(Buffer.from(k,"base64")),Qt.end()})}function Pt(){if(S)return;y=new Dd(t,{headers:{authorization:`Bearer ${e}`}}),y.on("open",async()=>{let Y=null;try{Y=await s?.()||null}catch{Y=null}q({v:Rt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Pi,providers:GO(),...Y?{agents:Y}:{}})}),y.on("message",Y=>{let $;try{$=JSON.parse(Y)}catch{return}if($.ch==="ctl"&&$.type==="hello_ok"){b=!0,E=uk,O=null,l.log(`[plane-link] connected to plane as ${n}`),$.minNodeVersion&&Pi&&KO(Pi,$.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${Pi}; the plane asks for \u2265${$.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),C&&clearInterval(C),C=setInterval(()=>q({v:Rt,ch:"ctl",type:"ping"}),ZO),C.unref?.(),h(),A().catch(z=>l.error("[plane-link] backfill failed:",z?.message||z));return}if($.ch==="ctl"&&$.type==="usage_caps"){ni($.caps??{},{source:"plane",updatedAt:$.updatedAt??Date.now()}).then(z=>l.log(`[plane-link] spend caps ${Object.keys(z.caps).length?`updated (${Object.keys(z.caps).join(", ")})`:"cleared"} from plane`)).catch(z=>l.error("[plane-link] spend-caps push rejected:",z?.message||z));return}if($.ch==="events"&&$.type==="ack"&&Array.isArray($.ids)){for(let z of $.ids){let xt=w.get(z);xt&&(w.delete(z),(m[xt.trackId]??0)<xt.seq&&(m[xt.trackId]=xt.seq))}lt();return}if($.ch==="control"&&$.type==="http_open"){W($).catch(z=>q({v:Rt,ch:"control",type:"http_end",reqId:$.reqId,error:z?.message||"tunnel error"}));return}if($.ch==="control"&&$.type==="http_cancel"){J.get($.reqId)?.abort();return}if($.ch==="attach"&&$.type==="open"){let z=new Dd(u+$.path,Array.isArray($.protocols)?$.protocols:[],$.host?{headers:{host:$.host}}:void 0);f.set($.streamId,z),d.set($.streamId,[]),z.on("open",()=>{let ht=d.get($.streamId)||[];d.delete($.streamId);for(let{buf:k,bin:P}of ht)z.send(k,{binary:P})}),z.on("message",(ht,k)=>{let P=k?ht:Buffer.from(String(ht));q({v:Rt,ch:"attach",type:"data",streamId:$.streamId,data:P.toString("base64"),...k?{bin:!0}:{}})});let xt=()=>{d.delete($.streamId),f.delete($.streamId)&&q({v:Rt,ch:"attach",type:"close",streamId:$.streamId})};z.on("close",xt),z.on("error",xt);return}if($.ch==="attach"&&$.type==="data"){let z=f.get($.streamId);if(!z)return;let xt=Buffer.from($.data,"base64"),ht=$.bin===!0,k=d.get($.streamId);k?k.push({buf:xt,bin:ht}):z.send(xt,{binary:ht});return}if($.ch==="attach"&&$.type==="close"){let z=f.get($.streamId);f.delete($.streamId),d.delete($.streamId),z?.close();return}});let K=()=>{b&&l.log("[plane-link] disconnected from plane"),b=!1,C&&(clearInterval(C),C=null),v&&(clearInterval(v),v=null),w.clear();for(let z of f.values())z.close();f.clear(),d.clear();for(let z of J.values())z.abort();if(J.clear(),S||nt)return;O==null&&(O=Date.now());let Y=Date.now()-O<zO,$=Y?JO+Math.random()*XO:E;nt=setTimeout(()=>{nt=null,Pt()},$),nt.unref?.(),Y||(E=Math.min(E*2,VO))};y.on("close",K),y.on("error",K)}return Ut.on("event",_),B().then(Pt),{stop:()=>{S=!0,Ut.off("event",_),nt&&clearTimeout(nt),R&&clearTimeout(R),C&&clearInterval(C),v&&clearInterval(v),q({v:Rt,ch:"ctl",type:"bye",reason:"shutdown"}),y?.close()},status:()=>({connected:b,nodeId:n,cursor:{...m},inflight:w.size})}}import tM from"node:fs";import pk from"node:fs/promises";import $s from"node:os";import eM from"node:path";var nM=1024*1024,Bi=t=>Math.round(t/nM);async function rM(t){try{let e=await pk.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:Bi(r),diskTotalMb:Bi(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function oM(){try{return(await Oe()).filter(e=>e.isBostrat).length}catch{return null}}async function sM(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await pk.readdir(eM.join(Rs,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function mk({stateDir:t=Rs,now:e=Date.now}={}){let n=$s.totalmem(),[r,o,s,a]=await Promise.all([_a().catch(()=>null),rM(tM.existsSync(t)?t:$s.tmpdir()),oM(),sM()]),i=$s.cpus()?.length||null,c=$s.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?Bi(n-r):null,memTotalMb:Bi(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round($s.uptime())}}function jd(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 hk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),wk=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Bd=t=>Math.round(t*1e4)/1e4;async function gk(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=hk(),u=0,f=[];for(let d of t||[]){let p=await L(d),m=he(p,{boxLabels:jd(d)});if(m.turns===0){d.archivedAt?u++:f.push(d.name||d.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.unpricedTurns||0;for(let w of m.unpricedModels||[])c.add(w);wk(l,m.totalTokens);for(let[w,y]of Object.entries(m.byModel))n[w]||={usd:0,events:0,tokens:hk()},n[w].usd=Bd(n[w].usd+y.usd),n[w].events+=y.events,wk(n[w].tokens,y.tokens);for(let[w,y]of Object.entries(m.byProvider))r[w]||={usd:0,events:0},r[w].usd=Bd(r[w].usd+y.usd),r[w].events+=y.events;e.push({id:d.id,name:d.name,usd:m.totalUsd,rollup:m})}return e.sort((d,p)=>p.usd-d.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Bd(o),turns:s,repricedTurns:a,unpricedTurns:i,unpricedModels:[...c],totalTokens:l,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:u+f.length,tracksSilentArchived:u,tracksSilentLive:f},tracks:e}}var Os=t=>`$${t.toFixed(4)}`,lo=(t,e)=>String(t).padEnd(e);function yk(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 ${Os(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) imputed at current rates (recorded before their model was priced)`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${lo(a,26)} ${lo(Os(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(` ${lo(a.name||a.id,30)} ${Os(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${lo(c.label,40)} ${lo(Os(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))}${lo(Os(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
393
- `)}import uM from"node:fs/promises";import dM from"node:path";var ji=t=>t.payload?.ts??t.ts,aM=12e4,In=t=>Math.round(t*1e4)/1e4,iM=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function cM(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>ji(S)>=e,a=vr([No(n?.name),No(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let E=vr(S.payload.task);E&&i.set(S.boxId,E)}let c=new Map,l=S=>{let E=S||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((S,E)=>ji(S)-ji(E)),d=0,p=0,m=0,w={turns:0};for(let S of f){let E=l(S.boxId),O=E.segments[E.segments.length-1],nt=ji(S);switch(E.lastTs!=null&&nt>E.lastTs&&s(S)&&(O.activeMs+=Math.min(nt-E.lastTs,aM)),E.lastTs=nt,S.provider&&(E.provider=S.provider),S.type){case"turn.user":{let R=S.payload?.text;if(S.source==="hook"||!String(R||"").trim())break;let C=u(vr(R));E.segments.push(C),s(S)&&(C.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let R=S.payload?.tool;R&&(O.tools[R]=(O.tools[R]||0)+1),s(S)&&(O.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let R=S.payload||{},C={input:R.tokens?.input||0,output:R.tokens?.output||0,cacheRead:R.tokens?.cacheRead||0,cacheWrite:R.tokens?.cacheWrite??R.tokens?.cacheCreation??0},v=Number(R.usd)||0;!v&&R.tokens&&(v=me(R.model,C)),!v&&R.tokens&&!Ta(R.model)&&(w.turns+=1),O.usd+=v,O.tokens.input+=C.input,O.tokens.output+=C.output,O.tokens.cacheRead+=C.cacheRead,O.tokens.cacheWrite+=C.cacheWrite;let D=R.model||"unknown";O.models[D]=(O.models[D]||0)+v,S.provider&&(O.provider=S.provider);break}case"handoff.done":s(S)&&(d+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,b=!1;for(let[S,E]of c)for(let O of E.segments){if(!(O.usd>0||O.activeMs>0||O.turns>0||O.toolCalls>0))continue;let R=O.textCat?.category??i.get(S)?.category??Cp(O.tools)?.category??a?.category??"uncategorized";y.has(R)||y.set(R,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let C=y.get(R);C.usd+=O.usd,C.activeMs+=O.activeMs,C.turns+=O.turns,C.toolCalls+=O.toolCalls;for(let[D,B]of Object.entries(O.models))C.models[D]=(C.models[D]||0)+B;let v=O.provider||E.provider;v&&(C.providers[v]=(C.providers[v]||0)+O.usd),v==="cursor"&&O.usd===0&&(b=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:b,unpricedTurns:w.turns}}function kk(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let w of t||[]){let{track:y,events:b}=w;if(!b?.length){a+=1;continue}let{perCategory:S,handoffs:E,merged:O,failed:nt,sawCursorActivity:R,unpricedTurns:C}=cM(w,{since:r});R&&(i=!0),c+=C;let v={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:O,subtasksFailed:nt};for(let[D,B]of S){v.usd+=B.usd,v.activeMs+=B.activeMs,v.turns+=B.turns,v.categories[D]={usd:In(B.usd),activeMs:B.activeMs};for(let[T,g]of Object.entries(B.models))v.models[T]=In((v.models[T]||0)+g);for(let[T,g]of Object.entries(B.providers))v.providers[T]=In((v.providers[T]||0)+g);o.has(D)||o.set(D,iM());let lt=o.get(D);lt.usd+=B.usd,lt.activeMs+=B.activeMs,lt.turns+=B.turns,lt.toolCalls+=B.toolCalls;for(let[T,g]of Object.entries(B.models))lt.models[T]=(lt.models[T]||0)+g;for(let[T,g]of Object.entries(B.providers))lt.providers[T]=(lt.providers[T]||0)+g;let q=lt.byTrack.get(y?.id)||{usd:0,activeMs:0};q.usd+=B.usd,q.activeMs+=B.activeMs,lt.byTrack.set(y?.id,q)}(v.usd>0||v.activeMs>0||v.turns>0)&&(v.usd=In(v.usd),v.dominantCategory=lM(v.categories),s.push(v))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((t||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,y])=>({category:w,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:In(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:bk(y.models),providers:bk(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((b,S)=>S[1].usd-b[1].usd||S[1].activeMs-b[1].activeMs).slice(0,2).map(([b,S])=>({trackId:b,name:p.get(b),usd:In(S.usd),activeMs:S.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:In(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var lM=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},bk=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,In(n)]));var uo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function fM(t){let e=String(t||"7d");return e in uo?{key:e,ms:uo[e]}:{key:"7d",ms:uo["7d"]}}function pM(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=si(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 mM(t){if(!t?.cwd)return null;try{let n=(await uM.readFile(dM.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 xk(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}:fM(e),f=[];for(let d of t||[]){let p=await L(d),m=pM(d,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:m,brief:await mM(d)})}return{window:l,...kk(f,{windowMs:u,now:c})}}var Rn=36e5,Ak=24*Rn,hM=400,wM=12,fo=t=>Math.round(t*1e4)/1e4;function gM({window:t,from:e,to:n,now:r=Date.now()}={}){let o=Number(e),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(t||"7d")in uo?String(t||"7d"):"7d",i=uo[a];return{key:a,since:i==null?null:r-i,until:r}}var yM=(t,e)=>t!=null&&e-t<=48*Rn?Rn:Ak;function Sk(t,e){if(e===Rn)return Math.floor(t/Rn)*Rn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function bM(t,e){if(e===Rn)return t+Rn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var vk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Tk=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},kM=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,fo(n)]));async function _k(t,e={}){let n=e.now??Date.now(),r=gM({...e,now:n}),o=r.since==null?Ak:yM(r.since,r.until),s=v=>(r.since==null||v>=r.since)&&v<r.until,a=r.since!=null?r.until-r.since:null,i=v=>a!=null&&v>=r.since-a&&v<r.since,c={},l={},u=vk(),f=0,d=0,p=0,m=0,w=0,y=new Set,b=new Map,S=[],E=[],O=[],nt=null;for(let v of t||[]){let B=(await L(v)).filter(T=>T.type==="cost.recorded");if(!B.length){v.archivedAt||O.push(v.name||v.id);continue}let lt=[];for(let T of B){let g=T.ts??0;s(g)?lt.push(T):i(g)&&(w+=Ln(T).usd)}if(!lt.length)continue;let q=he(lt,{boxLabels:jd(v)});f+=q.totalUsd,d+=q.turns,p+=q.repricedTurns||0,m+=q.unpricedTurns||0;for(let T of q.unpricedModels||[])y.add(T);Tk(u,q.totalTokens);for(let[T,g]of Object.entries(q.byModel))c[T]||={usd:0,events:0,tokens:vk()},c[T].usd=fo(c[T].usd+g.usd),c[T].events+=g.events,Tk(c[T].tokens,g.tokens);for(let[T,g]of Object.entries(q.byProvider))l[T]||={usd:0,events:0},l[T].usd=fo(l[T].usd+g.usd),l[T].events+=g.events;for(let T of lt){let{usd:g}=Ln(T);if(!g)continue;let h=Sk(T.ts??0,o),A=b.get(h)??{t:h,total:0,byProvider:{}};A.total+=g;let _=T.provider||"unknown";A.byProvider[_]=(A.byProvider[_]||0)+g,b.set(h,A),(nt==null||h<nt)&&(nt=h)}S.push({id:v.id,name:v.name||v.id,archived:!!v.archivedAt,usd:q.totalUsd,turns:q.turns,providers:Object.keys(q.byProvider)});for(let[T,g]of Object.entries(q.byBox))E.push({trackId:v.id,trackName:v.name||v.id,boxId:T,...g})}let R=r.since!=null?Sk(r.since,o):nt,C=[];for(let v=R,D=0;v!=null&&v<r.until&&D<hM;v=bM(v,o),D++){let B=b.get(v);C.push(B?{t:v,total:fo(B.total),byProvider:kM(B.byProvider)}:{t:v,total:0,byProvider:{}})}return S.sort((v,D)=>D.usd-v.usd),E.sort((v,D)=>D.usd-v.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:C,fleet:{totalUsd:fo(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:O},prior:a!=null?{totalUsd:fo(w),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:E.slice(0,wM)}}import po from"node:fs/promises";import xM from"node:fs";import Nt from"node:path";import SM from"node:os";import{execFile as vM}from"node:child_process";import{promisify as TM}from"node:util";var AM=TM(vM),Ek=process.env.BOSTRAT_STATE_DIR||Nt.join(process.env.HOME||SM.tmpdir(),".bostrat"),Li=!1,Ld=null;async function Fd(t,e){let n=await po.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await po.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await po.mkdir(Nt.dirname(e),{recursive:!0}),await po.copyFile(t,e),!0)}var _M=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Fi(t,e,{skip:n=null}={}){let r=0,o=await po.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(_M.has(s.name)||n?.has(s.name))continue;let a=Nt.join(t,s.name);s.isDirectory()?r+=await Fi(a,Nt.join(e,s.name)):s.isFile()&&(r+=await Fd(a,Nt.join(e,s.name))?1:0)}return r}async function Ik({stateDir:t=Ek,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Ek}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await po.mkdir(e,{recursive:!0});let o=0;o+=await Fd(Nt.join(t,"tracks.json"),Nt.join(e,"tracks.json"))?1:0,o+=await Fd(Nt.join(t,"events.jsonl"),Nt.join(e,"events.jsonl"))?1:0,o+=await Fi(Nt.join(t,"events"),Nt.join(e,"events")),o+=await Fi(Nt.join(t,"memory"),Nt.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Fi(Nt.join(u.cwd,".bostrat"),Nt.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>AM("git",["-C",e,...u]);xM.existsSync(Nt.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 dc(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||Li))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Li&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Li=!1,Ld=null}catch(u){Li=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
394
- `).slice(-3).join(" | ").slice(0,300);f!==Ld&&(Ld=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 EM}from"node:child_process";import{promisify as IM}from"node:util";var RM=IM(EM),$M=12e4,OM=6e4;function MM(t){return`refs/bostrat/wip/${t}`}function Ok(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function CM(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:$M}var Ui=(t,e,n=15e3)=>RM("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),Rk=new Map,$k=new Set;async function NM(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Ui(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Ui(r,["remote","get-url","origin"])}catch{return $k.has(r)||($k.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 ss(r,"bostrat: wip snapshot"),{stdout:s}=await Ui(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Rk.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=MM(n);return await Ui(r,["push","--force","origin",`${o}:${i}`],OM),Rk.set(r,a),await I({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
395
- `)[0]}`),{pushed:!1,reason:"push-failed"}}}function Mk({listTracks:t,sweepMs:e=CM(),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 NM(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 PM}from"node:child_process";import{promisify as DM}from"node:util";import{randomUUID as BM}from"node:crypto";import jM from"node:fs";import LM from"node:os";import FM from"node:path";var On=DM(PM),Hi=t=>new Promise(e=>setTimeout(e,t)),Mn=Ic,Ud=process.env.BOSTRAT_USAGE_PROBE_CWD||FM.join(LM.homedir(),".bostrat","usage-probe"),UM=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,HM=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,WM=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Ck=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,$n=null,Ms=null,Cs=null;async function qM(){try{return await On(M,N("has-session","-t",Mn)),!0}catch{return!1}}function KM(){$n&&clearTimeout($n),$n=setTimeout(()=>GM().catch(()=>{}),UM),$n.unref&&$n.unref()}async function GM(){$n&&(clearTimeout($n),$n=null);try{await On(M,N("kill-session","-t",Mn))}catch{}}async function VM(){let t=Date.now()+WM;for(;;){let e=await Hd().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await Hi(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Hi(500)}}async function zM(){if(await qM())return;if(!await sn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");jM.mkdirSync(Ud,{recursive:!0}),await tn(Ud);let t=BM();await On(M,N("new-session","-d","-s",Mn,"-c",Ud,"-x","200","-y","60",...Ec()));let e=`${_c} --session-id ${t}`;await On(M,N("send-keys","-t",Mn,e,"Enter")),await VM()}async function Hd(){let{stdout:t}=await On(M,N("capture-pane","-t",Mn,"-p"));return t}async function JM(){await zM(),await On(M,N("send-keys","-t",Mn,"-l","/usage")),await On(M,N("send-keys","-t",Mn,"Enter"));let t=Date.now()+HM,e="";for(;await Hi(400),e=await Hd(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await Hi(900),e=await Hd(),await On(M,N("send-keys","-t",Mn,"Escape")).catch(()=>{}),KM(),e}function XM(t){let e=String(t||"").split(`
396
- `).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 YM(t){let e=String(t||"").split(`
397
- `).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 Nk({fresh:t=!1}={}){return!t&&Ms&&Date.now()-Ms.at<Ck?Ms.data:Cs||(Cs=(async()=>{try{let e=await JM(),n=XM(e),r={...n,contributors:YM(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ms={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 Ms={at:Date.now()-(Ck-3e3),data:n},n}finally{Cs=null}})(),Cs)}var ZM=262144;function QM(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 Pk(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:QM(t.resets_in_seconds)}:null}function tC(t){let e=String(t||"").split(`
398
- `),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:Pk(i.primary),week:Pk(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 Wd(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Yn(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 Ft(n.file,ZM),o=tC(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 Dk(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 Wi({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 Wd(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Wd(null))}return n.has("cursor")&&r.push(await Wi({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Wi(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?Wd(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Nk({fresh:e})}}function eC(t=process.env){return!!(te(t)&&t.BOSTRAT_PLANE_TOKEN)}var nC=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),qi=(t,e)=>Object.assign(new Error(t),{status:e});async function Bk(t,e){if(!eC(e))throw qi("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${te(e)}${t}`,{headers:nC(e)})}catch(o){throw qi(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw qi(r?.error||`plane replied ${n.status}`,n.status);return r}async function jk(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await Bk(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function Lk(t,{env:e=process.env}={}){if(!t)throw qi("message id required",400);return(await Bk(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function qd(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 rC(t){let[e,n]=t.split("/"),r=qd(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 Fk(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(rC).filter(Boolean)}function oC(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function Uk(t){let e=Fk(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:oC,allowed(r){if(n)return!0;let o=qd((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function Hk(t,e){let n=Fk(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=qd(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import Wk from"node:crypto";import Ki from"node:fs";import sC from"node:path";function qk(t=process.env,{stateDir:e=Rs,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=sC.join(e,"api-token");try{let a=Ki.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=Wk.randomBytes(32).toString("base64url");try{Ki.mkdirSync(e,{recursive:!0}),Ki.writeFileSync(o,s+`
399
- `,{mode:384}),Ki.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Ns(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 Kk(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:Wk.timingSafeEqual(n,r)}function Gk(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 aC=[/^\/api\/github\/token\b/],iC=t=>aC.some(e=>e.test(t));function Vk({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=Gk(o);return Kk(c,t)?a():iC(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||Ns(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 zk(t,e,{enforce:n=!0}={}){return!n||Kk(Gk(t),e)?!0:Ns(t)}import cC from"node:http";import lC from"node:net";function Jk(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 Gi(t){let e=Jk(t.headers.host);return e?{slug:e,view:cl(e)}:null}var uC=t=>`<!doctype html><meta charset="utf-8">
388
+ _${o} archived_`:"")}async function vO(t,e){let n=await zn();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?(dr(lk(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:kO(u,i)}}case"archive":{if(!a.length)return pt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Nd(await r.listTracks(),a.join(" "));return i?(dr(gO(t,n,{track:i,responseUrl:e.response_url})),pt(`Archiving *${Y(i.name)}*\u2026`)):Pd(c,a.join(" "))}case"handoff":{let i=r.knownAgents??wO,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}=Nd(await r.listTracks(),l);return u?(dr(yO(t,n,{track:u,agent:c,responseUrl:e.response_url})),pt(`Handing *${Y(u.name)}* to ${Y(c)}\u2026`)):Pd(f,l)}case"stop":{if(!a.length)return pt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Nd(await r.listTracks(),a.join(" "));return i?(dr(bO(t,n,{track:i,responseUrl:e.response_url})),pt(`Stopping *${Y(i.name)}*\u2026`)):Pd(c,a.join(" "))}case"list":return pt(await xO(r));default:return pt(hO)}}async function SO(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Ld(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 Ze(),await fo(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(l||"question")}~
389
+ \u2713 *${Y(a)}*`}}]}).catch(()=>{})}}catch(c){await qt(n.response_url,pt(`Couldn't answer: ${c?.message||c}`))}}async function TO(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,li(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 qt(n.response_url,pt(`Couldn't act on the offer: ${c?.message||c}`))}}async function AO(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await zn();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await qt(e.response_url,{...pt(`Starting *${Y(s)}*\u2026`),replace_original:!0}),dr(lk(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"?dr(SO(t,r,e,o)):n.action_id==="bostrat-ship"&&dr(TO(t,r,e,o))}async function _O(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 pO(e.channel,e.thread_ts);if(!n)return;let r=await zn();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await fr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function EO(t){return async e=>{if(e.type==="slash_commands")return vO(t,e.payload);if(e.type==="interactive"){await AO(t,e.payload);return}if(e.type==="events_api"){await _O(t,e.payload?.event);return}}}var IO=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 RO(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:Os(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 $O(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await fo(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 fr(t,e,r.question||"The lead needs an answer",RO(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Ze()}async function OO(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 Ze(),await fo(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(s||"question")}~
390
+ \u2713 _answered_`}}]})}async function MO(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 fr(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Y(Os(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await Ze()}async function CO(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await Ze();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await fo(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function NO(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??PO)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await rk(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 fr(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 PO(t,e){let n=await pn(t);if(!n)return null;let r=Ne(n,e);if(!r)return null;let o=await $s.readFile(zt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function DO(t,e){if(!IO.has(e.type))return;let n=await Ld(e.trackId);if(!n)return;let r=await zn();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 Ze(),await Bd(r,n);return;case"turn.agent":{if(!o.text)return;let s=mO(o.text);if(!s)return;await fr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Y(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Wa(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return $O(r,n,e);case"prompt.answered":return OO(r,n,e);case"pr.offered":return MO(r,n,e);case"pr.offer.answered":return CO(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 fr(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 Ze(),await Bd(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await fr(r,n,`Live preview ${o.name}: ${Ka(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Y(o.name)}*: ${Ka(o.slug)}
391
+ _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return NO(r,n,e,t.deps)}}function uk(t){let e={deps:t};Ft.on("event",s=>{DO(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await zn()||(r&&clearInterval(r),r=null,n=ok({getToken:async()=>(await zn())?.appToken,onEnvelope:EO(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import BO from"node:fs/promises";function jO(t){let e=new Map;for(let n of String(t||"").split(`
392
+ `)){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 LO(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}function FO(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Oe(e.cwd,e.sessionId):e?.cwd?Fc(e.cwd):null}async function dk(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await R(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":await i({type:"turn.user",actor:"user",payload:{promptHead:String(t.prompt??t.user_prompt??"").slice(0,160)||null}});break;case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:{tool:t.tool_name??null}});break;case"Stop":await UO(r,o,t,i);break;default:break}return a}async function UO(t,e,n,r){let o=[],s=FO(n,e);if(s)try{o=jO(await BO.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=LO(await L(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:dn(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 qO from"node:fs";import Fd from"node:fs/promises";import KO from"node:http";import po from"node:path";import Fi from"node:os";import{createRequire as GO}from"node:module";import Ud from"ws";import HO from"node:path";import WO from"node:os";var Ms=process.env.BOSTRAT_STATE_DIR||HO.join(process.env.HOME||WO.tmpdir(),".bostrat");function fk(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 pk(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 ji=(()=>{try{return GO(import.meta.url)("../package.json").version||null}catch{return null}})(),VO=process.env.BOSTRAT_STATE_DIR||po.join(process.env.HOME||Fi.tmpdir(),".bostrat");function zO(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 JO({home:t=process.env.HOME||Fi.homedir(),env:e=process.env}={}){let n=r=>{try{return qO.statSync(r),!0}catch{return!1}};return{claude:n(po.join(t,".claude",".credentials.json")),codex:n(po.join(e.CODEX_HOME||po.join(t,".codex"),"auth.json"))}}var It=1,mk=1e3,XO=3e4,YO=6e4,ZO=1500,QO=500,Li=100,tM=2e3,eM=25e3,nM=6e4,rM=6e4;function hk({url:t,token:e,nodeId:n=Fi.hostname(),name:r=Fi.hostname(),listTracks:o,detectAgents:s=null,sampleMetrics:a=null,metricsMs:i=rM,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=pk(t);let u=c.replace(/^http/,"ws"),f=new Map,d=new Map,p=po.join(VO,"plane-link","cursor.json"),m={},w={},y=new Map,k=null,v=!1,E=!1,$=mk,z=null,O=null,P=null,S=null,C=null,D=!1;async function it(){try{m=JSON.parse(await Fd.readFile(p,"utf8"))}catch{m={}}}function ct(){D=!0,!P&&(P=setTimeout(async()=>{if(P=null,!!D){D=!1;try{await Fd.mkdir(po.dirname(p),{recursive:!0}),await Fd.writeFile(p,JSON.stringify(m))}catch(G){l.error("[plane-link] cursor flush failed:",G?.message||G)}}},tM),P.unref?.())}function T(G){return!k||k.readyState!==Ud.OPEN?!1:(k.send(JSON.stringify(G)),!0)}function g(G){let B=Date.now();for(let{trackId:b,event:_}of G){let lt=y.get(_.id);y.set(_.id,{trackId:b,seq:_.seq,event:_,sentAt:B,tries:(lt?.tries??0)+1})}return T({v:It,ch:"events",type:"append",events:G})}function h(G){let B=null;for(let b of y.values())b.trackId===G&&(B==null||b.seq<B)&&(B=b.seq);return B}function A(G){let B=w[G];if(B==null)return;let b=h(G),_=b==null?B:Math.min(B,b-1);_>(m[G]??0)&&(m[G]=_,ct())}function I(){if(!v)return;let G=Date.now(),B=[...y.values()].filter(b=>b.event&&G-b.sentAt>=nM);if(B.length){B.sort((b,_)=>b.trackId===_.trackId?b.seq-_.seq:b.trackId<_.trackId?-1:1),l.log(`[plane-link] re-sending ${B.length} unacked event(s) the plane never confirmed`);for(let b=0;b<B.length;b+=Li){let _=B.slice(b,b+Li).map(lt=>({trackId:lt.trackId,event:lt.event}));if(!g(_))return}}}async function J(){if(!a)return;let G;try{G=await a()}catch(B){l.error("[plane-link] metrics sample failed:",B?.message||B);return}G&&T({v:It,ch:"ctl",type:"metrics",metrics:G})}function W(){a&&(C&&clearInterval(C),J(),C=setInterval(J,i),C.unref?.())}async function st(){let G;try{G=await o()}catch(B){l.error("[plane-link] backfill listTracks failed:",B?.message||B);return}T({v:It,ch:"ctl",type:"tracks",ids:G.map(B=>B.id).filter(B=>typeof B=="string")});for(let B of G){if(!v)return;let b;try{b=await L(B)}catch{continue}let _=m[B.id]??0,lt=b.filter(nt=>nt.seq>_&&typeof nt.id=="string");for(let nt=0;nt<lt.length;nt+=Li){let bt=lt.slice(nt,nt+Li).map(Dt=>({trackId:B.id,event:Dt}));if(!g(bt))return}}}function Ct(G){!v||!G?.trackId||typeof G.id!="string"||g([{trackId:G.trackId,event:G}])}let vt=new Map;async function Ks(G){let{reqId:B,method:b,path:_,headers:lt={},body:nt}=G,bt=Object.fromEntries(Object.entries(lt).filter(([,ue])=>ue!=null)),Dt=new AbortController;vt.set(B,Dt);try{if(G.host){await pr(G,bt,Dt);return}let ue=await fetch(c+_,{method:b,headers:bt,body:nt?Buffer.from(nt,"base64"):void 0,signal:Dt.signal}),tn=Object.fromEntries(ue.headers);for(let Zt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete tn[Zt];if(tn["content-type"]||(tn["content-type"]="application/octet-stream"),T({v:It,ch:"control",type:"http_head",reqId:B,status:ue.status,headers:tn}),ue.body){for await(let Zt of ue.body)if(!T({v:It,ch:"control",type:"http_data",reqId:B,data:Buffer.from(Zt).toString("base64")}))break}T({v:It,ch:"control",type:"http_end",reqId:B,error:null})}finally{vt.delete(B)}}function pr(G,B,b){let{reqId:_,method:lt,path:nt,body:bt}=G,Dt=new URL(c);return new Promise(ue=>{let tn=KO.request({host:Dt.hostname,port:Dt.port,method:lt,path:nt,headers:{...B,host:G.host},signal:b.signal},Zt=>{let{connection:JN,"keep-alive":XN,"transfer-encoding":YN,upgrade:ZN,...ex}=Zt.headers;T({v:It,ch:"control",type:"http_head",reqId:_,status:Zt.statusCode||502,headers:ex}),Zt.on("data",nx=>{T({v:It,ch:"control",type:"http_data",reqId:_,data:Buffer.from(nx).toString("base64")})||Zt.destroy()}),Zt.on("end",()=>{T({v:It,ch:"control",type:"http_end",reqId:_,error:null}),ue()}),Zt.on("error",()=>{T({v:It,ch:"control",type:"http_end",reqId:_,error:"view upstream error"}),ue()})});tn.on("error",Zt=>{T({v:It,ch:"control",type:"http_end",reqId:_,error:Zt?.message||"view request failed"}),ue()}),bt&&tn.write(Buffer.from(bt,"base64")),tn.end()})}function Gs(){if(E)return;k=new Ud(t,{headers:{authorization:`Bearer ${e}`}}),k.on("open",async()=>{let B=null;try{B=await s?.()||null}catch{B=null}T({v:It,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:ji,providers:JO(),...B?{agents:B}:{}})}),k.on("message",B=>{let b;try{b=JSON.parse(B)}catch{return}if(b.ch==="ctl"&&b.type==="hello_ok"){v=!0,$=mk,z=null,l.log(`[plane-link] connected to plane as ${n}`),b.minNodeVersion&&ji&&zO(ji,b.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${ji}; the plane asks for \u2265${b.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),S&&clearInterval(S),S=setInterval(()=>{T({v:It,ch:"ctl",type:"ping"}),I()},eM),S.unref?.(),W(),st().catch(_=>l.error("[plane-link] backfill failed:",_?.message||_));return}if(b.ch==="ctl"&&b.type==="usage_caps"){si(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),(w[nt.trackId]??0)<nt.seq&&(w[nt.trackId]=nt.seq),_.add(nt.trackId))}for(let lt of _)A(lt);return}if(b.ch==="control"&&b.type==="http_open"){Ks(b).catch(_=>T({v:It,ch:"control",type:"http_end",reqId:b.reqId,error:_?.message||"tunnel error"}));return}if(b.ch==="control"&&b.type==="http_cancel"){vt.get(b.reqId)?.abort();return}if(b.ch==="attach"&&b.type==="open"){let _=new Ud(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:Dt}of nt)_.send(bt,{binary:Dt})}),_.on("message",(nt,bt)=>{let Dt=bt?nt:Buffer.from(String(nt));T({v:It,ch:"attach",type:"data",streamId:b.streamId,data:Dt.toString("base64"),...bt?{bin:!0}:{}})});let lt=()=>{d.delete(b.streamId),f.delete(b.streamId)&&T({v:It,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),C&&(clearInterval(C),C=null),y.clear(),w={};for(let _ of f.values())_.close();f.clear(),d.clear();for(let _ of vt.values())_.abort();if(vt.clear(),E||O)return;z==null&&(z=Date.now());let B=Date.now()-z<YO,b=B?ZO+Math.random()*QO:$;O=setTimeout(()=>{O=null,Gs()},b),O.unref?.(),B||($=Math.min($*2,XO))};k.on("close",G),k.on("error",G)}return Ft.on("event",Ct),it().then(Gs),{stop:()=>{E=!0,Ft.off("event",Ct),O&&clearTimeout(O),P&&clearTimeout(P),S&&clearInterval(S),C&&clearInterval(C),T({v:It,ch:"ctl",type:"bye",reason:"shutdown"}),k?.close()},status:()=>({connected:v,nodeId:n,cursor:{...m},inflight:y.size})}}import oM from"node:fs";import wk from"node:fs/promises";import Cs from"node:os";import sM from"node:path";var aM=1024*1024,Ui=t=>Math.round(t/aM);async function iM(t){try{let e=await wk.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:Ui(r),diskTotalMb:Ui(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function cM(){try{return(await $e()).filter(e=>e.isBostrat).length}catch{return null}}async function lM(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await wk.readdir(sM.join(Ms,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function gk({stateDir:t=Ms,now:e=Date.now}={}){let n=Cs.totalmem(),[r,o,s,a]=await Promise.all([Ra().catch(()=>null),iM(oM.existsSync(t)?t:Cs.tmpdir()),cM(),lM()]),i=Cs.cpus()?.length||null,c=Cs.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?Ui(n-r):null,memTotalMb:Ui(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Cs.uptime())}}function Hd(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 yk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),bk=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Hi=t=>Math.round(t*1e4)/1e4;async function kk(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=yk(),f=0,d=[];for(let p of t||[]){let m=await L(p),w=me(m,{boxLabels:Hd(p)});if(w.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let y of w.unpricedModels||[])l.add(y);bk(u,w.totalTokens);for(let[y,k]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:yk()},n[y].usd=Hi(n[y].usd+k.usd),n[y].events+=k.events,bk(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=Hi(r[y].usd+k.usd),r[y].events+=k.events;e.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return e.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Hi(o),asRecordedUsd:Hi(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 $n=t=>`$${t.toFixed(4)}`,mo=(t,e)=>String(t).padEnd(e);function xk(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 ${$n(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 ${$n(e.asRecordedUsd)} \xB7 current-rates total ${$n(e.totalUsd)} (${e.totalUsd>=e.asRecordedUsd?"+":""}${$n(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(` ${mo(a,26)} ${mo($n(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(` ${mo(a.name||a.id,30)} ${$n(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${mo(c.label,40)} ${mo($n(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))}${mo($n(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
393
+ `)}import mM from"node:fs/promises";import hM from"node:path";var Wi=t=>t.payload?.ts??t.ts,uM=12e4,On=t=>Math.round(t*1e4)/1e4,dM=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function fM(t,{since:e}){let{track:n,events:r,brief:o}=t,s=v=>Wi(v)>=e,a=_r([jo(n?.name),jo(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 E=_r(v.payload.task);E&&i.set(v.boxId,E)}let c=new Map,l=v=>{let E=v||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},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,E)=>Wi(v)-Wi(E)),d=0,p=0,m=0,w={turns:0};for(let v of f){let E=l(v.boxId),$=E.segments[E.segments.length-1],z=Wi(v);switch(E.lastTs!=null&&z>E.lastTs&&s(v)&&($.activeMs+=Math.min(z-E.lastTs,uM)),E.lastTs=z,v.provider&&(E.provider=v.provider),v.type){case"turn.user":{let O=v.payload?.text;if(v.source==="hook"||!String(O||"").trim())break;let P=u(_r(O));E.segments.push(P),s(v)&&(P.turns+=1);break}case"tool.called":{if(v.source==="transcript")break;let O=v.payload?.tool;O&&($.tools[O]=($.tools[O]||0)+1),s(v)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(v))break;let{usd:O,tokens:P,model:S,unpriced:C}=Ce(v);C&&(w.turns+=1),$.usd+=O,P&&($.tokens.input+=P.input,$.tokens.output+=P.output,$.tokens.cacheRead+=P.cacheRead,$.tokens.cacheWrite+=P.cacheWrite),$.models[S]=($.models[S]||0)+O,v.provider&&($.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,E]of c)for(let $ of E.segments){if(!($.usd>0||$.activeMs>0||$.turns>0||$.toolCalls>0))continue;let O=$.textCat?.category??i.get(v)?.category??Dp($.tools)?.category??a?.category??"uncategorized";y.has(O)||y.set(O,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let P=y.get(O);P.usd+=$.usd,P.activeMs+=$.activeMs,P.turns+=$.turns,P.toolCalls+=$.toolCalls;for(let[C,D]of Object.entries($.models))P.models[C]=(P.models[C]||0)+D;let S=$.provider||E.provider;S&&(P.providers[S]=(P.providers[S]||0)+$.usd),S==="cursor"&&$.usd===0&&(k=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:k,unpricedTurns:w.turns}}function Sk(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let w of t||[]){let{track:y,events:k}=w;if(!k?.length){a+=1;continue}let{perCategory:v,handoffs:E,merged:$,failed:z,sawCursorActivity:O,unpricedTurns:P}=fM(w,{since:r});O&&(i=!0),c+=P;let S={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:$,subtasksFailed:z};for(let[C,D]of v){S.usd+=D.usd,S.activeMs+=D.activeMs,S.turns+=D.turns,S.categories[C]={usd:On(D.usd),activeMs:D.activeMs};for(let[T,g]of Object.entries(D.models))S.models[T]=On((S.models[T]||0)+g);for(let[T,g]of Object.entries(D.providers))S.providers[T]=On((S.providers[T]||0)+g);o.has(C)||o.set(C,dM());let it=o.get(C);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=On(S.usd),S.dominantCategory=pM(S.categories),s.push(S))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((t||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,y])=>({category:w,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:On(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:vk(y.models),providers:vk(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:On(v.usd),activeMs:v.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:On(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var pM=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},vk=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,On(n)]));var ho={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function wM(t){let e=String(t||"7d");return e in ho?{key:e,ms:ho[e]}:{key:"7d",ms:ho["7d"]}}function gM(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=ci(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 yM(t){if(!t?.cwd)return null;try{let n=(await mM.readFile(hM.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 Tk(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}:wM(e),f=[];for(let d of t||[]){let p=await L(d),m=gM(d,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:m,brief:await yM(d)})}return{window:l,...Sk(f,{windowMs:u,now:c})}}var Mn=36e5,Ik=24*Mn,bM=400,kM=12,wo=t=>Math.round(t*1e4)/1e4;function xM({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 ho?String(t||"7d"):"7d",i=ho[a];return{key:a,since:i==null?null:r-i,until:r}}var vM=(t,e)=>t!=null&&e-t<=48*Mn?Mn:Ik;function Ak(t,e){if(e===Mn)return Math.floor(t/Mn)*Mn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function SM(t,e){if(e===Mn)return t+Mn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var _k=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Ek=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},TM=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,wo(n)]));async function Rk(t,e={}){let n=e.now??Date.now(),r=xM({...e,now:n}),o=r.since==null?Ik:vM(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=_k(),f=0,d=0,p=0,m=0,w=0,y=new Set,k=new Map,v=[],E=[],$=[],z=null;for(let S of t||[]){let D=(await L(S)).filter(T=>T.type==="cost.recorded");if(!D.length){S.archivedAt||$.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)&&(w+=Ce(T).usd)}if(!it.length)continue;let ct=me(it,{boxLabels:Hd(S)});f+=ct.totalUsd,d+=ct.turns,p+=ct.repricedTurns||0,m+=ct.unpricedTurns||0;for(let T of ct.unpricedModels||[])y.add(T);Ek(u,ct.totalTokens);for(let[T,g]of Object.entries(ct.byModel))c[T]||={usd:0,events:0,tokens:_k()},c[T].usd=wo(c[T].usd+g.usd),c[T].events+=g.events,Ek(c[T].tokens,g.tokens);for(let[T,g]of Object.entries(ct.byProvider))l[T]||={usd:0,events:0},l[T].usd=wo(l[T].usd+g.usd),l[T].events+=g.events;for(let T of it){let{usd:g}=Ce(T);if(!g)continue;let h=Ak(T.ts??0,o),A=k.get(h)??{t:h,total:0,byProvider:{}};A.total+=g;let I=T.provider||"unknown";A.byProvider[I]=(A.byProvider[I]||0)+g,k.set(h,A),(z==null||h<z)&&(z=h)}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))E.push({trackId:S.id,trackName:S.name||S.id,boxId:T,...g})}let O=r.since!=null?Ak(r.since,o):z,P=[];for(let S=O,C=0;S!=null&&S<r.until&&C<bM;S=SM(S,o),C++){let D=k.get(S);P.push(D?{t:S,total:wo(D.total),byProvider:TM(D.byProvider)}:{t:S,total:0,byProvider:{}})}return v.sort((S,C)=>C.usd-S.usd),E.sort((S,C)=>C.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:P,fleet:{totalUsd:wo(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:v.length,tracksSilentLive:$},prior:a!=null?{totalUsd:wo(w),since:r.since-a,until:r.since}:null,tracks:v.slice(0,50),boxes:E.slice(0,kM)}}import go from"node:fs/promises";import AM from"node:fs";import Mt from"node:path";import _M from"node:os";import{execFile as EM}from"node:child_process";import{promisify as IM}from"node:util";var RM=IM(EM),$k=process.env.BOSTRAT_STATE_DIR||Mt.join(process.env.HOME||_M.tmpdir(),".bostrat"),qi=!1,Wd=null;async function qd(t,e){let n=await go.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await go.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await go.mkdir(Mt.dirname(e),{recursive:!0}),await go.copyFile(t,e),!0)}var $M=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Ki(t,e,{skip:n=null}={}){let r=0,o=await go.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if($M.has(s.name)||n?.has(s.name))continue;let a=Mt.join(t,s.name);s.isDirectory()?r+=await Ki(a,Mt.join(e,s.name)):s.isFile()&&(r+=await qd(a,Mt.join(e,s.name))?1:0)}return r}async function Ok({stateDir:t=$k,dest:e=process.env.BOSTRAT_SYNC_DIR||`${$k}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await go.mkdir(e,{recursive:!0});let o=0;o+=await qd(Mt.join(t,"tracks.json"),Mt.join(e,"tracks.json"))?1:0,o+=await qd(Mt.join(t,"events.jsonl"),Mt.join(e,"events.jsonl"))?1:0,o+=await Ki(Mt.join(t,"events"),Mt.join(e,"events")),o+=await Ki(Mt.join(t,"memory"),Mt.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Ki(Mt.join(u.cwd,".bostrat"),Mt.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>RM("git",["-C",e,...u]);AM.existsSync(Mt.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 wc(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,Wd=null}catch(u){qi=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
394
+ `).slice(-3).join(" | ").slice(0,300);f!==Wd&&(Wd=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 OM}from"node:child_process";import{promisify as MM}from"node:util";var CM=MM(OM),NM=12e4,PM=6e4;function DM(t){return`refs/bostrat/wip/${t}`}function Nk(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function BM(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:NM}var Gi=(t,e,n=15e3)=>CM("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),Mk=new Map,Ck=new Set;async function jM(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Gi(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Gi(r,["remote","get-url","origin"])}catch{return Ck.has(r)||(Ck.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 cs(r,"bostrat: wip snapshot"),{stdout:s}=await Gi(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Mk.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=DM(n);return await Gi(r,["push","--force","origin",`${o}:${i}`],PM),Mk.set(r,a),await R({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(`
395
+ `)[0]}`),{pushed:!1,reason:"push-failed"}}}function Pk({listTracks:t,sweepMs:e=BM(),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 jM(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 LM}from"node:child_process";import{promisify as FM}from"node:util";import{randomUUID as UM}from"node:crypto";import HM from"node:fs";import WM from"node:os";import qM from"node:path";var Nn=FM(LM),Vi=t=>new Promise(e=>setTimeout(e,t)),Pn=Cc,Kd=process.env.BOSTRAT_USAGE_PROBE_CWD||qM.join(WM.homedir(),".bostrat","usage-probe"),KM=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,GM=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,VM=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Dk=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Cn=null,Ns=null,Ps=null;async function zM(){try{return await Nn(M,N("has-session","-t",Pn)),!0}catch{return!1}}function JM(){Cn&&clearTimeout(Cn),Cn=setTimeout(()=>XM().catch(()=>{}),KM),Cn.unref&&Cn.unref()}async function XM(){Cn&&(clearTimeout(Cn),Cn=null);try{await Nn(M,N("kill-session","-t",Pn))}catch{}}async function YM(){let t=Date.now()+VM;for(;;){let e=await Gd().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await Vi(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Vi(500)}}async function ZM(){if(await zM())return;if(!await an())throw new Error("host tmux server not running \u2014 start globo-tmux.service");HM.mkdirSync(Kd,{recursive:!0}),await en(Kd);let t=UM();await Nn(M,N("new-session","-d","-s",Pn,"-c",Kd,"-x","200","-y","60",...Mc()));let e=`${Oc} --session-id ${t}`;await Nn(M,N("send-keys","-t",Pn,e,"Enter")),await YM()}async function Gd(){let{stdout:t}=await Nn(M,N("capture-pane","-t",Pn,"-p"));return t}async function QM(){await ZM(),await Nn(M,N("send-keys","-t",Pn,"-l","/usage")),await Nn(M,N("send-keys","-t",Pn,"Enter"));let t=Date.now()+GM,e="";for(;await Vi(400),e=await Gd(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await Vi(900),e=await Gd(),await Nn(M,N("send-keys","-t",Pn,"Escape")).catch(()=>{}),JM(),e}function tC(t){let e=String(t||"").split(`
396
+ `).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 eC(t){let e=String(t||"").split(`
397
+ `).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 Bk({fresh:t=!1}={}){return!t&&Ns&&Date.now()-Ns.at<Dk?Ns.data:Ps||(Ps=(async()=>{try{let e=await QM(),n=tC(e),r={...n,contributors:eC(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ns={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 Ns={at:Date.now()-(Dk-3e3),data:n},n}finally{Ps=null}})(),Ps)}var nC=262144;function rC(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 jk(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:rC(t.resets_in_seconds)}:null}function oC(t){let e=String(t||"").split(`
398
+ `),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:jk(i.primary),week:jk(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 Vd(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Qn(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 Lt(n.file,nC),o=oC(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 Lk(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 zi({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 Vd(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Vd(null))}return n.has("cursor")&&r.push(await zi({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function zi(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?Vd(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Bk({fresh:e})}}function sC(t=process.env){return!!(Qt(t)&&t.BOSTRAT_PLANE_TOKEN)}var aC=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),Ji=(t,e)=>Object.assign(new Error(t),{status:e});async function Fk(t,e){if(!sC(e))throw Ji("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Qt(e)}${t}`,{headers:aC(e)})}catch(o){throw Ji(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Ji(r?.error||`plane replied ${n.status}`,n.status);return r}async function Uk(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await Fk(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function Hk(t,{env:e=process.env}={}){if(!t)throw Ji("message id required",400);return(await Fk(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function zd(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 iC(t){let[e,n]=t.split("/"),r=zd(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 Wk(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(iC).filter(Boolean)}function cC(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function qk(t){let e=Wk(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:cC,allowed(r){if(n)return!0;let o=zd((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function Kk(t,e){let n=Wk(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=zd(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import Gk from"node:crypto";import Xi from"node:fs";import lC from"node:path";function Vk(t=process.env,{stateDir:e=Ms,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=lC.join(e,"api-token");try{let a=Xi.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=Gk.randomBytes(32).toString("base64url");try{Xi.mkdirSync(e,{recursive:!0}),Xi.writeFileSync(o,s+`
399
+ `,{mode:384}),Xi.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Ds(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 zk(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:Gk.timingSafeEqual(n,r)}function Jk(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 uC=[/^\/api\/github\/token\b/],dC=t=>uC.some(e=>e.test(t));function Xk({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=Jk(o);return zk(c,t)?a():dC(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||Ds(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 Yk(t,e,{enforce:n=!0}={}){return!n||zk(Jk(t),e)?!0:Ds(t)}import fC from"node:http";import pC from"node:net";function Zk(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 Yi(t){let e=Zk(t.headers.host);return e?{slug:e,view:pl(e)}:null}var mC=t=>`<!doctype html><meta charset="utf-8">
400
400
  <meta http-equiv="refresh" content="3">
401
401
  <title>${t.name} \u2014 starting</title>
402
402
  <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
403
403
  <div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
404
- <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function Xk(t,e,n){let r=Gi(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(uC(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=cC.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 Yk(t,e,n){let r=Gi(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
404
+ <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function Qk(t,e,n){let r=Yi(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(mC(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=fC.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 t0(t,e,n){let r=Yi(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
405
405
  \r
406
406
  bostrat: no live view "${s}"\r
407
- `),e.destroy(),!0;let a=lC.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
407
+ `),e.destroy(),!0;let a=pC.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
408
408
  `)+`\r
409
409
  \r
410
- `),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 Zk(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}.${dC(n.host)}/`}function dC(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 Jk(e)&&cl(n[0])?n.slice(1).join("."):e}var Qk=10,t0=200,fC=120,pC=24,mC=/^\d{4}-\d{2}-\d{2}$/;function Kd(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var hC=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function e0(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 wC(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Uo(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(`
411
- `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,fC)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${pC}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>t0)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${t0}-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>Qk)throw new Error(`${r.length} bullets \u2014 over the ${Qk}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function n0(t,{repos:e=Kd()}={}){let n=new Set(e);return(await ve()).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 r0(t,{md:e,day:n=null}){let r=wC(e),o=n==null||n===""?hC():String(n);if(!mC.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await I({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}import{execFile as gC}from"node:child_process";import{promisify as yC}from"node:util";var Vd=yC(gC),o0=3e4,bC=30*6e4,kC=o0,xC=10*6e4,SC=10080*6e4,vC=60*6e4,TC=8,AC=1440*6e4,_C=3,s0="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Gd=60*6e4,EC=15*6e4,Vi=new Map,zi=new Map,IC=/could not resolve|not found|no pull requests?/i,RC=t=>IC.test(String(t?.message||t||""));function $C(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Gd)}var a0={fast:kC,slow:xC,decay:vC};function i0(t,e){return t.armedAt&&e-t.armedAt<bC||$C(t,e)?"fast":e-(t.firstSeen||0)>SC?"decay":"slow"}function OC(t,e){return a0[i0(t,e)]}function js(t,e,n,r){let o=t.lanes?.get(V(e.repo,e.number))||{};return gi({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function MC(t,e){Ge("watch");try{let{stdout:n}=await Vd("gh",["pr","view",String(e),"--repo",t,"--json",s0],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await de({slug:t})}});return JSON.parse(n||"null")}catch(n){throw kn(n)&&xn(n),n}}async function CC(t){Ge("watch");try{let{stdout:e}=await Vd("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",s0],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await de({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw kn(e)&&xn(e),e}}async function NC(){let t=new Map;for(let e of await at().catch(()=>[]))t.set(e.id,e);return t}async function PC(t,e){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function DC(t,e){let n=await sr(t);return n?Xr(n,e):null}async function BC(t,e,n){let r=await sr(t);if(!r)throw new Error(`no local checkout for ${t}`);return Yr(r,e,{method:n})}async function jC(t,e){let n=await sr(t);if(!n)throw new Error(`no local checkout for ${t}`);return hs(n,e)}async function LC(t,e){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function mo(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 FC=10*6e4;async function UC(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>AC){await mo(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 mo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await mo(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<FC)return;await mo(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 mo(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 mo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await c0(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Ps(t,e,n){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function HC(t,e){try{return await Vd("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ds(t,e,n,r=()=>!0){return(await L(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var Bs=(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 WC(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 qa(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Ga(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<EC;await Kh(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await qh(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ds(a,"pushed",t.number,f=>f.payload?.sha===s)&&await Ps(a,"pushed",{...Bs(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Gd)await Ka(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 Ka(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=js(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Ds(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await Ps(a,"ci-fixed",{...Bs(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Ka(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Gd)await Va(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Va(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){js(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Ds(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Ps(a,"reconciled",{...Bs(t,e),base:i.baseRefName||null}).catch(()=>{}),await Va(t.repo,t.number).catch(()=>{})}}}}async function qC(t,e,n,r){let o=js(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Ds(s,"closed",t.number)&&await Ps(s,"closed",{...Bs(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 KC(t,e,n){js(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Ds(r,"lost",t.number)&&await Ps(r,"lost",Bs(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function c0(t,e,n,r,o={}){let s=js(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await L(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 GC(t={}){let{now:e=Date.now(),auth:n=Ku,view:r=MC,list:o=CC,links:s=Vh,trackMap:a=NC,emitMerged:i=PC,mark:c=wn,archiveIfArmed:l=vn,notifyIfArmed:u=Tn,notifyIfResolved:f=bd,batchThreshold:d=_C,detail:p=DC,merge:m=BC,updateBranch:w=jC,lookup:y=Bt,disarm:b=Yo,emitDisarmed:S=LC,localHasCommit:E=HC}=t;if(!(await n().catch(()=>null))?.authenticated||li()>0)return 0;let R=(await s().catch(()=>[])).filter(T=>e-(Vi.get(V(T.repo,T.number))||0)>=OC(T,e));if(!R.length)return 0;let C=Date.now(),v=new Map,D=new Map;for(let T of R){let g=i0(T,e);v.set(V(T.repo,T.number),{lane:g,intervalMs:a0[g]}),Vi.set(V(T.repo,T.number),e),D.has(T.repo)||D.set(T.repo,[]),D.get(T.repo).push(T)}let B=await a().catch(()=>new Map),lt={now:e,lanes:v,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:w,lookup:y,disarm:b,emitDisarmed:S,localHasCommit:E};for(let[T,g]of D){let h,A=new Set;if(g.length>d)h=await o(T).catch(()=>null);else{h=new Map;for(let _ of g)try{let J=await r(T,_.number);J?.number!=null&&h.set(Number(J.number),J)}catch(J){RC(J)&&A.add(V(_.repo,_.number))}}if(h)for(let _ of g){let J=V(_.repo,_.number),W=h.get(Number(_.number));if(!W){if(A.has(J)){let it=(zi.get(J)||0)+1;zi.set(J,it),it>=TC&&(await KC(_,B,lt),zi.delete(J),Vi.delete(J))}continue}zi.delete(J),W.state==="MERGED"||W.state==="CLOSED"?(W.state==="MERGED"?await c0(_,W,B,lt):await qC(_,W,B,lt),Vi.delete(J)):(await WC(_,W,B,lt).catch(()=>{}),_.autoMerge&&await UC(_,B,lt).catch(()=>{}))}}let q={};for(let{lane:T}of v.values())q[T]=(q[T]||0)+1;return console.log(`[pr-watch] probed ${R.length} link(s) (${Object.entries(q).map(([T,g])=>`${T} ${g}`).join(", ")}) in ${Date.now()-C}ms`),R.length}function l0(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await GC()}catch{}finally{t=!1}}},o0)}import Qe from"node:fs/promises";import ho from"node:path";import w0 from"node:os";import u0 from"node:fs/promises";import Ji from"node:path";import{execFile as VC}from"node:child_process";import{promisify as zC}from"node:util";import{fileURLToPath as JC}from"node:url";var XC=zC(VC),f0=Ji.dirname(JC(import.meta.url)),YC=Ji.resolve(f0,"..","..","client","dist"),ZC="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",QC=new Map([...ZC].map((t,e)=>[t,e]));function tN(t){let e=[],n=0,r=0;for(let o of t){let s=QC.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 eN(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=tN(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 nN(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 d0=new Map;async function rN(t){let e;try{e=await u0.stat(t)}catch{return null}let n=d0.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await u0.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:eN(r.mappings||"")};return d0.set(t,o),o}catch{return null}}var zd=null;function oN(){return zd||(zd=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):XC("git",["rev-parse","--short","HEAD"],{cwd:Ji.resolve(f0,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),zd}var sN=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function aN(t,{distDir:e=YC}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(sN)){let[a,i,c,l]=s,u=await rN(Ji.join(e,"assets",`${i}.map`)),f=u?nN(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 p0(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await oN();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await aN(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var iN=()=>process.env.BOSTRAT_STATE_DIR||ho.join(process.env.HOME||w0.tmpdir(),".bostrat"),Cn=()=>ho.join(iN(),"telemetry"),g0=()=>ho.join(Cn(),".relay-offsets.json"),cN=50,lN=14,uN=3e4,m0=500,Zt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function dN(t,e){if(!t||typeof t!="object")return null;let n=Zt(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:Zt(t.anonId,64),sessionId:Zt(t.sessionId,64),userId:Zt(t.userId,64),release:Zt(t.release,64),appVersion:Zt(t.appVersion,32),route:Zt(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:Zt(t.error.kind,256),message:Zt(t.error.message,8192),stack:Zt(t.error.stack,32768),fingerprint:Zt(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:Zt(s?.n,64),r:Zt(s?.r,128)}))),o}var y0=t=>new Date(t).toISOString().slice(0,10),h0=0;async function b0(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,cN):[]).map(s=>dN(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await p0(s);await Qe.mkdir(Cn(),{recursive:!0});let o=ho.join(Cn(),`${y0(e)}.ndjson`);return await Qe.appendFile(o,r.map(s=>JSON.stringify(s)+`
412
- `).join(""),"utf8"),e-h0>6*3600*1e3&&(h0=e,fN(e).catch(()=>{})),r.length}async function fN(t=Date.now()){let e=y0(t-lN*24*3600*1e3),n=[];try{n=await Qe.readdir(Cn())}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 Qe.rm(ho.join(Cn(),s),{force:!0});let o=await x0();for(let s of r)delete o[s];await S0(o)}var Xi=new Map;function k0(t,e=Date.now()){let n=t||"unknown",r=Xi.get(n);return r||(Xi.size>512&&Xi.clear(),r={tokens:40,at:e},Xi.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 x0(){try{return JSON.parse(await Qe.readFile(g0(),"utf8"))||{}}catch{return{}}}async function S0(t){await Qe.mkdir(Cn(),{recursive:!0}),await Qe.writeFile(g0(),JSON.stringify(t),"utf8")}function pN(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||w0.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 mN({fetchImpl:t=fetch}={}){let e=await fh();if(!e)return{sent:0,disabled:!0};let n;try{n=(await Qe.readdir(Cn())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await x0(),o=0;for(let s of n){let a=ho.join(Cn(),s),i=r[s]||0,c;try{let d=await Qe.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(`
413
- `).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=m0){let p=f.slice(d,d+m0).map(w=>pN(w)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await S0(r)}return{sent:o,disabled:!1}}var Jd=null,Xd=!1;function v0(){Jd||(Jd=setInterval(async()=>{if(!Xd){Xd=!0;try{await mN()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Xd=!1}}},uN),Jd.unref?.())}import Zi from"node:fs/promises";import I0 from"node:path";import hN from"node:os";var R0=()=>process.env.BOSTRAT_STATE_DIR||I0.join(process.env.HOME||hN.tmpdir(),".bostrat"),$0=()=>I0.join(R0(),"dictation-glossary.json"),wN=100,gN=500,yN=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Qi=null,Yd=null,Zd=Promise.resolve();function T0(){let t={},e=Date.now();for(let n of yN)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function O0(){return Qi||(Yd??=Zi.readFile($0(),"utf8").then(t=>JSON.parse(t)?.terms??T0()).catch(()=>T0()).then(t=>Qi=t).finally(()=>Yd=null),Yd)}async function A0(){let t=JSON.stringify({terms:Qi},null,2),e=$0(),n=`${e}.${process.pid}.tmp`;await Zi.mkdir(R0(),{recursive:!0}),await Zi.writeFile(n,t),await Zi.rename(n,e)}function bN(){return Zd=Zd.then(A0,A0),Zd.catch(()=>{})}async function Qd(){return Object.values(await O0()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var kN=t=>Math.max(1,Math.ceil(t.length/4));async function xN(){let t=[],e=0;for(let{term:n}of await Qd()){let r=kN(n);if(!(e+r>gN)&&(t.push(n),e+=r,t.length>=wN))break}return t}async function SN(t,e){let n=await O0(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var _0=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Yi=t=>t.toLowerCase();function E0(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function M0(t,e){let n=_0(t),r=_0(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]=Yi(n[u])===Yi(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;)Yi(n[c])===Yi(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(E0(r[l])&&i.add(r[l]),l++);for(;l<s;l++)E0(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await SN(u,"learned");return await bN(),[...i]}var Ls=null;function vN(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 C0(){if(!Ls)return null;let t=await Wo();if(!t||t.apiKey.slice(-6)!==Ls.keyTail)return null;let{keyTail:e,...n}=Ls;return n}async function N0(t,e){let n=await Wo();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 xN())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=vN(o.status,i);c.persistent&&(Ls={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 Ls=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var tf={"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 TN(t){if(!t)return null;let e=String(t).trim();if(tf[e])return e;let n=null;for(let r of Object.keys(tf))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function AN(t,e={}){let n=tf[TN(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 _N(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 Fs=null;async function EN(){if(!Fs)return null;let t=await La();if(!t||t.apiKey.slice(-6)!==Fs.keyTail)return null;let{keyTail:e,...n}=Fs;return n}async function IN(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=_N(e.status,o);s.persistent&&(Fs={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}Fs=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 RN={openai:IN};async function P0(){let t=await La();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await EN()}:{configured:!1}}async function D0(){let t=await La();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 RN[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function B0(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=AN(e,s),i=await I(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var W0=ft.dirname(ON(import.meta.url)),j0=Number(process.env.PORT)||3362,tc=ck(process.env),q0=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,wo=Uk(process.env.BOSTRAT_ALLOW_IPS),sf=Hk(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Gi),K0=qk(),G0=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=Ie();x.disable("x-powered-by");x.use((t,e,n)=>{if(Ns(t)||wo.allowed(wo.clientIp(t))||sf.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(Xk);x.use(Vk({token:K0,enforce:G0}));x.use("/api/mocks",Ie.json({limit:"1mb"}));x.use("/api/mocks",Ie.raw({type:"image/png",limit:"24mb"}));x.use("/api/runbooks",Ie.json({limit:"1mb"}));x.use("/api/telemetry",Ie.json({limit:"256kb"}));x.use("/api/hook",Ie.json({limit:"2mb"}));x.use("/api/dictation",Ie.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",Ie.raw({type:()=>!0,limit:"25mb"}));x.use(Ie.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:zt,hostTmuxAlive:await sn(),autoTriggerPct:q0})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await Oe()})});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 Bn(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(!bt(n))return e.status(400).json({error:"invalid session name"});if(!await rt(n))return e.status(404).json({error:"session not found"});try{await pe(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(!bt(n))return e.status(400).json({error:"invalid session name"});let r=await rt(n);if(!r)return e.status(404).json({error:"session not found"});let o=await Oo(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(!bt(n))return e.status(400).json({error:"invalid session name"});try{let r=await am(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=va(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 L0=10;x.get("/api/tracks",async(t,e)=>{let n=await at(),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>L0){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,L0).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:_i,agentAvailability:await pd(),tickets:await Ua()})});x.get("/api/cost",async(t,e)=>{try{let n=await at();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await gk(n);if(r.cursorUsage=Uu(),t.query.format==="text")return e.type("text/plain").send(yk(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 xk(await at(),{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 _k(await at(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=Uu(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=is(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json(is())});x.post("/api/usage/caps",async(t,e)=>{try{e.json(await ni(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 Dk(await at(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs",async(t,e)=>{try{e.json(await Qh(await at()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs/:key",async(t,e)=>{try{let n=await tw(await at(),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 Lb()})}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 ff(t.get(qs),{byToken:mb,legacy:()=>pb(t.body?.session_id,t.body?.cwd)});if(n==="refused")return Ks("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&&Ks("mismatch",{track:r.track.id,box:r.box.name}),Ks(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await ik(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(!k0(wo.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await b0(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 Re(),r=new Set((await Promise.all(n.map(a=>ot(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await xo()).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 Js(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:zs(),calls:Gg(),latency:ky()})});x.post("/api/github/refresh",(t,e)=>{Sf(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await jk(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 Lk(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function af(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 ec({repo:t=null,number:e=null,branch:n=null}){let r=t?await ot(t):null;if(r&&e!=null){let s=await Bt(r,e),a=s&&await et(s.trackId);if(a)return a}return n?(await at()).find(s=>s.workspace?.branch===n&&af(s,t,r))??null:null}async function V0({repo:t=null,number:e=null,branch:n=null}){let r=await ec({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await ot(t).catch(()=>null):null;return(await at()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&af(a,t,o))??null}async function ue(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await ec({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await ot(s).catch(()=>null):null);await I({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function PN(t){let e=(await at()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await ot(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&&af(a,n.path,r));s&&await Xt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}x.get("/api/git/prs",async(t,e)=>{try{let n=await Ey();await PN(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 w=o||"merge";if(!ms.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let b=await ot((await Ve(n)).path);if(!b)throw new Error("repo has no GitHub remote");let S=await ec({repo:n,number:y,branch:l}).catch(()=>null);if(!S)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await hs(n,y),await Xt({repo:b,number:y,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Jo(b,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Jn(b,y,i===!0),await Xo(b,y,c===!0&&i!==!0),_e(),await ue("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:y,auto:!0})}let d=await Yr(n,r,{method:o||"squash"});await ue("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await ot(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Jn(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Xo(p,d.number,!0).catch(()=>{}),await Tn(p,d.number).catch(()=>{}),m=await vn(p,d.number).catch(()=>!1),await wn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let w=await ec({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await so(w.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 ot((await Ve(n)).path).catch(()=>null),l=c?await Yo(c,i).catch(()=>null):null;try{await zu(n,i)}catch(u){if(!l)throw u}_e(),await ue("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 ot(n).catch(()=>null),s=await Oy(n,{method:r||"merge",onMerged:a=>{ue("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Tn(o,a.number).catch(()=>{}).then(()=>vn(o,a.number)).catch(()=>{}).then(()=>wn(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:$y()})});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(!ms.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return e.status(400).json({error:"trackId required"});let c=await et(n);if(!c)return e.status(404).json({error:`Track ${n} isn't on this node`});let l=(await ve()).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 Jn(d.repo,d.number,o===!0).catch(()=>{}),await Xo(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 sr(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let w=await Xr(m,d.number);f.push({r:p,l:d,repoPath:m,pr:w,plan:Iy(w,{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:w,plan:y}of Ry(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Yr(m,p.number,{method:i}),await ue("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await Tn(p.repo,p.number).catch(()=>{}),await vn(p.repo,p.number).catch(()=>{}),await wn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await hs(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Jo(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await ue("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(b){d.ok=!1,d.action="failed",d.reason=b?.message||String(b)}if(_e(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Jn(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await Xo(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 et(n);if(!r)return e.status(404).json({error:`Track ${n} isn't on this node`});let o=(await ve()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Yo(a.repo,a.number).catch(()=>null),c=await sr(a.repo).catch(()=>null);c&&await zu(c,a.number).catch(()=>{}),i&&(await ue("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}))}_e(),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 et(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(w=>{let y=u.find(b=>b.name===w||b.repo===w||b.base===w||b.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(f=await us(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let y=await Xu(w.dir,{title:r,body:o,base:s}),b=w.repo||await ot(w.dir).catch(()=>null);b&&y.number&&await Xt({repo:b,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await ue("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??w.name}}),p.push({repo:w.name,slug:b??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(w=>w.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await Xu(i.cwd,{title:r,body:o,base:s}),l=await ot(i.cwd).catch(()=>null);l&&c.number&&await Xt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await ue("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 et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await us(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 $b(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 Ob(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 Xr(n,r),s=await ot(n).catch(()=>null),a=await V0({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(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await My(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(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
414
- ${b.log??"(no fetchable log \u2014 see link)"}
410
+ `),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 e0(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}.${hC(n.host)}/`}function hC(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 Zk(e)&&pl(n[0])?n.slice(1).join("."):e}var n0=10,r0=200,wC=120,gC=24,yC=/^\d{4}-\d{2}-\d{2}$/;function Jd(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var bC=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function o0(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 kC(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=qo(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(`
411
+ `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,wC)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${gC}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>r0)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${r0}-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 s0(t,{repos:e=Jd()}={}){let n=new Set(e);return(await ve()).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 a0(t,{md:e,day:n=null}){let r=kC(e),o=n==null||n===""?bC():String(n);if(!yC.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await R({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 xC}from"node:child_process";import{promisify as vC}from"node:util";var Yd=vC(xC),i0=3e4,SC=30*6e4,TC=i0,AC=10*6e4,_C=10080*6e4,EC=60*6e4,IC=8,RC=1440*6e4,$C=3,c0="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Xd=60*6e4,OC=15*6e4,Zi=new Map,Qi=new Map,MC=/could not resolve|not found|no pull requests?/i,CC=t=>MC.test(String(t?.message||t||""));function NC(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Xd)}var l0={fast:TC,slow:AC,decay:EC};function u0(t,e){return t.armedAt&&e-t.armedAt<SC||NC(t,e)?"fast":e-(t.firstSeen||0)>_C?"decay":"slow"}function PC(t,e){return l0[u0(t,e)]}function Fs(t,e,n,r){let o=t.lanes?.get(K(e.repo,e.number))||{};return ki({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function DC(t,e){Ge("watch");try{let{stdout:n}=await Yd("gh",["pr","view",String(e),"--repo",t,"--json",c0],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await de({slug:t})}});return JSON.parse(n||"null")}catch(n){throw vn(n)&&Sn(n),n}}async function BC(t){Ge("watch");try{let{stdout:e}=await Yd("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",c0],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await de({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw vn(e)&&Sn(e),e}}async function jC(){let t=new Map;for(let e of await ot().catch(()=>[]))t.set(e.id,e);return t}async function LC(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function FC(t,e){let n=await ir(t);return n?to(n,e):null}async function UC(t,e,n){let r=await ir(t);if(!r)throw new Error(`no local checkout for ${t}`);return eo(r,e,{method:n})}async function HC(t,e){let n=await ir(t);if(!n)throw new Error(`no local checkout for ${t}`);return ys(n,e)}async function WC(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function yo(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 qC=10*6e4;async function KC(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>RC){await yo(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 yo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await yo(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<qC)return;await yo(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 yo(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 yo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await d0(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Bs(t,e,n){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function GC(t,e){try{return await Yd("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function js(t,e,n,r=()=>!0){return(await L(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var Ls=(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 VC(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 Va(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Ja(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<OC;await Jh(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await zh(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await js(a,"pushed",t.number,f=>f.payload?.sha===s)&&await Bs(a,"pushed",{...Ls(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Xd)await za(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 za(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Fs(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await js(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await Bs(a,"ci-fixed",{...Ls(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await za(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Xd)await Xa(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Xa(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Fs(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await js(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Bs(a,"reconciled",{...Ls(t,e),base:i.baseRefName||null}).catch(()=>{}),await Xa(t.repo,t.number).catch(()=>{})}}}}async function zC(t,e,n,r){let o=Fs(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await js(s,"closed",t.number)&&await Bs(s,"closed",{...Ls(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 JC(t,e,n){Fs(n,t,"lost",null);let r=e.get(t.trackId);r&&!await js(r,"lost",t.number)&&await Bs(r,"lost",Ls(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function d0(t,e,n,r,o={}){let s=Fs(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await L(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 XC(t={}){let{now:e=Date.now(),auth:n=Xu,view:r=DC,list:o=BC,links:s=Yh,trackMap:a=jC,emitMerged:i=LC,mark:c=yn,archiveIfArmed:l=An,notifyIfArmed:u=_n,notifyIfResolved:f=Td,batchThreshold:d=$C,detail:p=FC,merge:m=UC,updateBranch:w=HC,lookup:y=Pt,disarm:k=ts,emitDisarmed:v=WC,localHasCommit:E=GC}=t;if(!(await n().catch(()=>null))?.authenticated||fi()>0)return 0;let O=(await s().catch(()=>[])).filter(T=>e-(Zi.get(K(T.repo,T.number))||0)>=PC(T,e));if(!O.length)return 0;let P=Date.now(),S=new Map,C=new Map;for(let T of O){let g=u0(T,e);S.set(K(T.repo,T.number),{lane:g,intervalMs:l0[g]}),Zi.set(K(T.repo,T.number),e),C.has(T.repo)||C.set(T.repo,[]),C.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:w,lookup:y,disarm:k,emitDisarmed:v,localHasCommit:E};for(let[T,g]of C){let h,A=new Set;if(g.length>d)h=await o(T).catch(()=>null);else{h=new Map;for(let I of g)try{let J=await r(T,I.number);J?.number!=null&&h.set(Number(J.number),J)}catch(J){CC(J)&&A.add(K(I.repo,I.number))}}if(h)for(let I of g){let J=K(I.repo,I.number),W=h.get(Number(I.number));if(!W){if(A.has(J)){let st=(Qi.get(J)||0)+1;Qi.set(J,st),st>=IC&&(await JC(I,D,it),Qi.delete(J),Zi.delete(J))}continue}Qi.delete(J),W.state==="MERGED"||W.state==="CLOSED"?(W.state==="MERGED"?await d0(I,W,D,it):await zC(I,W,D,it),Zi.delete(J)):(await VC(I,W,D,it).catch(()=>{}),I.autoMerge&&await KC(I,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()-P}ms`),O.length}function f0(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await XC()}catch{}finally{t=!1}}},i0)}import Qe from"node:fs/promises";import bo from"node:path";import b0 from"node:os";import p0 from"node:fs/promises";import tc from"node:path";import{execFile as YC}from"node:child_process";import{promisify as ZC}from"node:util";import{fileURLToPath as QC}from"node:url";var tN=ZC(YC),h0=tc.dirname(QC(import.meta.url)),eN=tc.resolve(h0,"..","..","client","dist"),nN="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",rN=new Map([...nN].map((t,e)=>[t,e]));function oN(t){let e=[],n=0,r=0;for(let o of t){let s=rN.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 sN(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=oN(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 aN(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 m0=new Map;async function iN(t){let e;try{e=await p0.stat(t)}catch{return null}let n=m0.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await p0.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:sN(r.mappings||"")};return m0.set(t,o),o}catch{return null}}var Zd=null;function cN(){return Zd||(Zd=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):tN("git",["rev-parse","--short","HEAD"],{cwd:tc.resolve(h0,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Zd}var lN=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function uN(t,{distDir:e=eN}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(lN)){let[a,i,c,l]=s,u=await iN(tc.join(e,"assets",`${i}.map`)),f=u?aN(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 w0(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await cN();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await uN(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var dN=()=>process.env.BOSTRAT_STATE_DIR||bo.join(process.env.HOME||b0.tmpdir(),".bostrat"),Dn=()=>bo.join(dN(),"telemetry"),k0=()=>bo.join(Dn(),".relay-offsets.json"),fN=50,pN=14,mN=3e4,g0=500,Yt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function hN(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 x0=t=>new Date(t).toISOString().slice(0,10),y0=0;async function v0(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,fN):[]).map(s=>hN(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await w0(s);await Qe.mkdir(Dn(),{recursive:!0});let o=bo.join(Dn(),`${x0(e)}.ndjson`);return await Qe.appendFile(o,r.map(s=>JSON.stringify(s)+`
412
+ `).join(""),"utf8"),e-y0>6*3600*1e3&&(y0=e,wN(e).catch(()=>{})),r.length}async function wN(t=Date.now()){let e=x0(t-pN*24*3600*1e3),n=[];try{n=await Qe.readdir(Dn())}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 Qe.rm(bo.join(Dn(),s),{force:!0});let o=await T0();for(let s of r)delete o[s];await A0(o)}var ec=new Map;function S0(t,e=Date.now()){let n=t||"unknown",r=ec.get(n);return r||(ec.size>512&&ec.clear(),r={tokens:40,at:e},ec.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 T0(){try{return JSON.parse(await Qe.readFile(k0(),"utf8"))||{}}catch{return{}}}async function A0(t){await Qe.mkdir(Dn(),{recursive:!0}),await Qe.writeFile(k0(),JSON.stringify(t),"utf8")}function gN(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||b0.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 yN({fetchImpl:t=fetch}={}){let e=await wh();if(!e)return{sent:0,disabled:!0};let n;try{n=(await Qe.readdir(Dn())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await T0(),o=0;for(let s of n){let a=bo.join(Dn(),s),i=r[s]||0,c;try{let d=await Qe.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(`
413
+ `).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=g0){let p=f.slice(d,d+g0).map(w=>gN(w)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await A0(r)}return{sent:o,disabled:!1}}var Qd=null,tf=!1;function _0(){Qd||(Qd=setInterval(async()=>{if(!tf){tf=!0;try{await yN()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{tf=!1}}},mN),Qd.unref?.())}import rc from"node:fs/promises";import O0 from"node:path";import bN from"node:os";var M0=()=>process.env.BOSTRAT_STATE_DIR||O0.join(process.env.HOME||bN.tmpdir(),".bostrat"),C0=()=>O0.join(M0(),"dictation-glossary.json"),kN=100,xN=500,vN=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],oc=null,ef=null,nf=Promise.resolve();function E0(){let t={},e=Date.now();for(let n of vN)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function N0(){return oc||(ef??=rc.readFile(C0(),"utf8").then(t=>JSON.parse(t)?.terms??E0()).catch(()=>E0()).then(t=>oc=t).finally(()=>ef=null),ef)}async function I0(){let t=JSON.stringify({terms:oc},null,2),e=C0(),n=`${e}.${process.pid}.tmp`;await rc.mkdir(M0(),{recursive:!0}),await rc.writeFile(n,t),await rc.rename(n,e)}function SN(){return nf=nf.then(I0,I0),nf.catch(()=>{})}async function rf(){return Object.values(await N0()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var TN=t=>Math.max(1,Math.ceil(t.length/4));async function AN(){let t=[],e=0;for(let{term:n}of await rf()){let r=TN(n);if(!(e+r>xN)&&(t.push(n),e+=r,t.length>=kN))break}return t}async function _N(t,e){let n=await N0(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var R0=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),nc=t=>t.toLowerCase();function $0(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function P0(t,e){let n=R0(t),r=R0(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]=nc(n[u])===nc(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;)nc(n[c])===nc(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:($0(r[l])&&i.add(r[l]),l++);for(;l<s;l++)$0(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await _N(u,"learned");return await SN(),[...i]}var Us=null;function EN(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 D0(){if(!Us)return null;let t=await Go();if(!t||t.apiKey.slice(-6)!==Us.keyTail)return null;let{keyTail:e,...n}=Us;return n}async function B0(t,e){let n=await Go();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 AN())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=EN(o.status,i);c.persistent&&(Us={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 Us=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var of={"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 IN(t){if(!t)return null;let e=String(t).trim();if(of[e])return e;let n=null;for(let r of Object.keys(of))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function RN(t,e={}){let n=of[IN(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 $N(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 Hs=null;async function ON(){if(!Hs)return null;let t=await Ha();if(!t||t.apiKey.slice(-6)!==Hs.keyTail)return null;let{keyTail:e,...n}=Hs;return n}async function MN(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=$N(e.status,o);s.persistent&&(Hs={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}Hs=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 CN={openai:MN};async function j0(){let t=await Ha();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await ON()}:{configured:!1}}async function L0(){let t=await Ha();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 CN[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function F0(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=RN(e,s),i=await R(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 G0=ft.dirname(PN(import.meta.url)),U0=Number(process.env.PORT)||3362,sc=fk(process.env),V0=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,ko=qk(process.env.BOSTRAT_ALLOW_IPS),uf=Kk(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Yi),z0=Vk(),J0=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=Ee();x.disable("x-powered-by");x.use((t,e,n)=>{if(Ds(t)||ko.allowed(ko.clientIp(t))||uf.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(Qk);x.use(Xk({token:z0,enforce:J0}));x.use("/api/mocks",Ee.json({limit:"1mb"}));x.use("/api/mocks",Ee.raw({type:"image/png",limit:"24mb"}));x.use("/api/runbooks",Ee.json({limit:"1mb"}));x.use("/api/telemetry",Ee.json({limit:"256kb"}));x.use("/api/hook",Ee.json({limit:"2mb"}));x.use("/api/dictation",Ee.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",Ee.raw({type:()=>!0,limit:"25mb"}));x.use(Ee.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Vt,hostTmuxAlive:await an(),autoTriggerPct:V0})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await $e()})});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 Fn(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(!yt(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 pe(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(!yt(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 Po(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(!yt(n))return e.status(400).json({error:"invalid session name"});try{let r=await lm(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=Ea(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 H0=10;x.get("/api/tracks",async(t,e)=>{let n=await ot(),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>H0){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,H0).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:Ri,agentAvailability:await yd(),tickets:await qa()})});x.get("/api/cost",async(t,e)=>{try{let n=await ot();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await kk(n);if(r.cursorUsage=Gu(),t.query.format==="text")return e.type("text/plain").send(xk(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 Tk(await ot(),{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 Rk(await ot(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=Gu(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=us(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json(us())});x.post("/api/usage/caps",async(t,e)=>{try{e.json(await si(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 Lk(await ot(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs",async(t,e)=>{try{e.json(await rw(await ot()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs/:key",async(t,e)=>{try{let n=await ow(await ot(),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 Wb()})}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 hf(t.get(zs),{byToken:yb,legacy:()=>gb(t.body?.session_id,t.body?.cwd)});if(n==="refused")return Js("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&&Js("mismatch",{track:r.track.id,box:r.box.name}),Js(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await dk(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(!S0(ko.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await v0(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 Ie(),r=new Set((await Promise.all(n.map(a=>et(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Ao()).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 Qs(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:Zs(),calls:Xg(),latency:Ty()})});x.post("/api/github/refresh",(t,e)=>{Af(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await Uk(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 Hk(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function df(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 ac({repo:t=null,number:e=null,branch:n=null}){let r=t?await et(t):null;if(r&&e!=null){let s=await Pt(r,e),a=s&&await Q(s.trackId);if(a)return a}return n?(await ot()).find(s=>s.workspace?.branch===n&&df(s,t,r))??null:null}async function X0({repo:t=null,number:e=null,branch:n=null}){let r=await ac({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 ot()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&df(a,t,o))??null}async function le(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await ac({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await et(s).catch(()=>null):null);await R({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function LN(t){let e=(await ot()).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 Pt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&df(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 Oy();await LN(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 w=o||"merge";if(!gs.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let k=await et((await Ve(n)).path);if(!k)throw new Error("repo has no GitHub remote");let v=await ac({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 ys(n,y),await Jt({repo:k,number:y,trackId:v.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Zo(k,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Yn(k,y,i===!0),await Qo(k,y,c===!0&&i!==!0),Ae(),await le("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 eo(n,r,{method:o||"squash"});await le("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 Yn(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Qo(p,d.number,!0).catch(()=>{}),await _n(p,d.number).catch(()=>{}),m=await An(p,d.number).catch(()=>!1),await yn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let w=await ac({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await lo(w.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 Ve(n)).path).catch(()=>null),l=c?await ts(c,i).catch(()=>null):null;try{await Qu(n,i)}catch(u){if(!l)throw u}Ae(),await le("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 Py(n,{method:r||"merge",onMerged:a=>{le("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&_n(o,a.number).catch(()=>{}).then(()=>An(o,a.number)).catch(()=>{}).then(()=>yn(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:Ny()})});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(!gs.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 ve()).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 Yn(d.repo,d.number,o===!0).catch(()=>{}),await Qo(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 ir(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let w=await to(m,d.number);f.push({r:p,l:d,repoPath:m,pr:w,plan:My(w,{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:w,plan:y}of Cy(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await eo(m,p.number,{method:i}),await le("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await _n(p.repo,p.number).catch(()=>{}),await An(p.repo,p.number).catch(()=>{}),await yn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await ys(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Zo(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await le("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.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(Ae(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Yn(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await Qo(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 ve()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ts(a.repo,a.number).catch(()=>null),c=await ir(a.repo).catch(()=>null);c&&await Qu(c,a.number).catch(()=>{}),i&&(await le("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}))}Ae(),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=Wt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let y=u.find(k=>k.name===w||k.repo===w||k.base===w||k.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(f=await ps(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let y=await ed(w.dir,{title:r,body:o,base:s}),k=w.repo||await et(w.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 le("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??w.name}}),p.push({repo:w.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(w=>w.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await ed(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 le("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 ps(n),o=new Map(r.map(a=>[a.base,a])),s=Wt(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 Nb(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 Pb(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 to(n,r),s=await et(n).catch(()=>null),a=await X0({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=Wt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await Dy(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 Kt.promises.mkdir(f,{recursive:!0}),await Kt.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
414
+ ${k.log??"(no fetchable log \u2014 see link)"}
415
415
  `).join(`
416
- `));let p=l.map(b=>b.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await le(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 Xt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Uh(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await ue("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(b=>b.name)}}),e.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(b=>b.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 Xr(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await ot(n).catch(()=>null),a=await V0({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 le(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),u=s;u&&(await Xt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Hh(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await ue("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=Cg.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 et(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await L(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 le(a.id,ai(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await I({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&xb(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 Us=t=>e=>({...e,url:Zk(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await Ma({trackId:t.query.track||void 0});e.json({views:n.map(Us(t)),maxLive:fn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await Km(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Us(t)(n),session:Rr(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 et(n):null;if(!i&&s&&(i=(await at()).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 ll({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:Us(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:Us(t)(await Gm(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:Us(t)(await Vm(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 zm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var DN=[["linear",Kn,wl],["jira",ja,ml]],ef=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await pl(),r={};for(let[o,s,a]of DN){let i=await s();if(!i){ef.delete(o),r[o]={configured:!1};continue}let c=ef.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},ef.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 pl()})}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 dh(o),i=n?await et(n):null;!i&&r&&(i=(await at()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ft.sep)))||null),i&&await I({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await et(n):null;if(!s&&r&&(s=(await at()).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 Cr(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 et(r):null;if(!o&&n&&(o=(await at()).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 _h(),ticket:await Se(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 Se(t.params.id)})});x.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Cr(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 Il(t.params.id)})});var rc=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 $h(t.params.id))}catch(n){let r=n?.message||String(n);e.status(rc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await Rl(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(rc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Oh(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(rc(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 et(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await Mh(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(rc(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 et(n):null;if(!s&&r&&(s=(await at()).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 Nh(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 Ch()})}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 et(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await Ph(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 Hn({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 dn(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 dn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ne(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(Jt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/html",async(t,e)=>{let n=await dn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ne(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(Jt(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?t.query:t.body||{},{trackId:o,name:s,cwd:a,width:i,group:c}=r;try{let l=o?await et(o):null;if(!l&&a&&(l=(await at()).find(p=>p.cwd&&(a===p.cwd||a.startsWith(p.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(d=>d.name===l.leadBox),f=await Am({trackId:l.id,name:s,html:n?null:r.html,image:n,cwd:l.cwd,width:i,group:c,box:l.leadBox,agent:u?.agent||null});e.json({ok:!0,mock:f})}catch(l){let u=l?.message||String(l);e.status(/required|invalid|must be/i.test(u)?400:500).json({error:u})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await _m(t.params.id,{html:n?null:(t.body||{}).html,image:n,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:r})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|invalid|must be/i.test(o)?400:500;e.status(s).json({error:o})}});x.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Im(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await le(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 Vy(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Rm(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 Hy({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 Wy(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 et(n):null;if(!a&&s&&(a=(await at()).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 qy({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 Ky(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 Gy(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 at(),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 Pa({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 dl(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 dl(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Qm(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(Le(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 et(n):null;if(!a&&s&&(a=(await at()).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 eh({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 nh(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 rh(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 oh(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=e0(t.query.since||null);e.json({since:n,repos:Kd(),prs:await n0(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 et(o):null;if(!a&&s&&(a=(await at()).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 r0(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 BN=120,nf=400,nc=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>nf)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${nf} cap; a notification is a nudge, put the detail in chat`});let c=Uo(`${n||""}
417
- ${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 et(a):null;if(!l&&s&&(l=(await at()).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,BN):l.name,body:f(i,nf),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await I(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),e.json({ok:!0,notification:d,delivered:nc?nc.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 N0(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 Wo(),terms:await Qd(),problem:await C0()})});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 M0(n,r)})});x.get("/api/tracks/:id/voice-chat",async(t,e)=>{if(!await et(t.params.id))return e.status(404).json({error:"track not found"});e.json(await P0())});x.post("/api/tracks/:id/voice-chat/session",async(t,e)=>{try{let n=await et(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 D0())}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 et(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 B0(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 yd({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Cr(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 et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:z0(_s(t.params.id)),subtasks:ao(t.params.id).map(Hs)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Bb(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await Il(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 so(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 Ub(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 md(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 et(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}
416
+ `));let p=l.map(k=>k.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await ce(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 Kh(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await le("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),e.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(k=>k.name),log: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 to(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 X0({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=Wt(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 ce(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 Gh(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await le("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=Bg.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 L(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 ce(a.id,li(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 R({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&&Ab(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 Ws=t=>e=>({...e,url:e0(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await Pa({trackId:t.query.track||void 0});e.json({views:n.map(Ws(t)),maxLive:mn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await Jm(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Ws(t)(n),session:Cr(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 ot()).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 ml({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:Ws(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:Ws(t)(await Xm(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:Ws(t)(await Ym(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 Zm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var FN=[["linear",Vn,xl],["jira",Ua,bl]],sf=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await yl(),r={};for(let[o,s,a]of FN){let i=await s();if(!i){sf.delete(o),r[o]={configured:!1};continue}let c=sf.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},sf.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 yl()})}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 hh(o),i=n?await Q(n):null;!i&&r&&(i=(await ot()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ft.sep)))||null),i&&await R({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});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 ot()).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 Br(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 ot()).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 $h(),ticket:await xe(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 xe(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 Br(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 Cl(t.params.id)})});var cc=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 Nh(t.params.id))}catch(n){let r=n?.message||String(n);e.status(cc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await Nl(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(cc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Ph(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(cc(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 Dh(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(cc(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 ot()).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 jh(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 Bh()})}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 Lh(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 qn({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 pn(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 pn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ne(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(zt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/html",async(t,e)=>{let n=await pn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ne(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(zt(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?t.query:t.body||{},{trackId:o,name:s,cwd:a,width:i,group:c}=r;try{let l=o?await Q(o):null;if(!l&&a&&(l=(await ot()).find(p=>p.cwd&&(a===p.cwd||a.startsWith(p.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(d=>d.name===l.leadBox),f=await Rm({trackId:l.id,name:s,html:n?null:r.html,image:n,cwd:l.cwd,width:i,group:c,box:l.leadBox,agent:u?.agent||null});e.json({ok:!0,mock:f})}catch(l){let u=l?.message||String(l);e.status(/required|invalid|must be/i.test(u)?400:500).json({error:u})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await $m(t.params.id,{html:n?null:(t.body||{}).html,image:n,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:r})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|invalid|must be/i.test(o)?400:500;e.status(s).json({error:o})}});x.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Mm(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await ce(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 Yy(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Cm(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 Gy({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 Vy(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 ot()).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 zy({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 Jy(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 Xy(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 ot(),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 ja({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 wl(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 wl(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=rh(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(Le(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 ot()).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 sh({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 ah(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 ih(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 ch(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=o0(t.query.since||null);e.json({since:n,repos:Jd(),prs:await s0(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 ot()).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 a0(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 UN=120,af=400,ic=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>af)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${af} cap; a notification is a nudge, put the detail in chat`});let c=qo(`${n||""}
417
+ ${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 ot()).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,UN):l.name,body:f(i,af),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await R(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),e.json({ok:!0,notification:d,delivered:ic?ic.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 B0(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 Go(),terms:await rf(),problem:await D0()})});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 P0(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 j0())});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 L0())}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 F0(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 Sd({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Br(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:Y0(Rs(t.params.id)),subtasks:uo(t.params.id).map(qs)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Ub(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await Cl(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 lo(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 Kb(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 bd(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}
418
418
  data: ${JSON.stringify(i)}
419
419
 
420
- `),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await L(n))i.seq>o&&r(i);let s=Hp(n.id,r),a=setInterval(()=>e.write(`: hb
420
+ `),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await L(n))i.seq>o&&r(i);let s=Kp(n.id,r),a=setInterval(()=>e.write(`: hb
421
421
 
422
- `),25e3);t.on("close",()=>{clearInterval(a),s()})});x.get("/api/tracks/:id/search",async(t,e)=>{let n=await et(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=Po(await L(n)),a=ga(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 Df()})});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 Bf(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 jN=["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(`
423
- `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await le(t.params.id,jN)})}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 Wb(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 le(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 et(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 Qy(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 et(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||!nd(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(ed(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 LN=/^[0-9a-fA-F-]{8,64}$/;x.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await et(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(!LN.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=Me(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=$N.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?Dc(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 gd(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 gb(t.params.id);e.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await hd(t.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code|no answer selected/i.test(a)?409:500;e.status(i).json({error:a})}});x.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await Pb({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 wb(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 Ab(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 et(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 Wi(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await _b(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 zb(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Hs(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:ao(t.params.id).map(Hs)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await hb(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:Hs(Td(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:Hs(Xb(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Hs(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 xd(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 Sb(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(z0(_s(t.params.id))??{running:!1})});function z0(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:ra(),paths:tp,trace:await Qf({limit:n})})});var rf=ft.resolve(W0,"..","..","client","dist");Gt.existsSync(rf)&&(x.use(Ie.static(rf)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ft.join(rf,"index.html"))));var J0=MN.createServer(x),FN=new NN({noServer:!0}),X0=(t,e,n)=>{if(!(Ns(t)||wo.allowed(wo.clientIp(t))||sf.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
422
+ `),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=Lo(await L(n)),a=xa(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 Lf()})});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 Ff(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 HN=["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(`
423
+ `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await ce(t.params.id,HN)})}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 Vb(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 ce(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 rb(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||!id(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(ad(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 WN=/^[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(!WN.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=Oe(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=NN.createInterface({input:Kt.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?Uc(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 vd(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 xb(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 kd(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 Lb({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 kb(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 Rb(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 zi(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await $b(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 Zb(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:qs(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:uo(t.params.id).map(qs)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await bb(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:qs(Rd(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:qs(tk(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function qs(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 _d(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 _b(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(Y0(Rs(t.params.id))??{running:!1})});function Y0(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:ia(),paths:rp,trace:await np({limit:n})})});var cf=ft.resolve(G0,"..","..","client","dist");Kt.existsSync(cf)&&(x.use(Ee.static(cf)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ft.join(cf,"index.html"))));var Z0=DN.createServer(x),qN=new jN({noServer:!0}),Q0=(t,e,n)=>{if(!(Ds(t)||ko.allowed(ko.clientIp(t))||uf.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
424
424
  \r
425
- `),e.destroy();return}if(Yk(t,e,n))return;if(!zk(t,K0,{enforce:G0})){e.write(`HTTP/1.1 401 Unauthorized\r
425
+ `),e.destroy();return}if(t0(t,e,n))return;if(!Yk(t,z0,{enforce:J0})){e.write(`HTTP/1.1 401 Unauthorized\r
426
426
  \r
427
- `),e.destroy();return}let r=pp(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
427
+ `),e.destroy();return}let r=wp(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
428
428
  \r
429
- `),e.destroy();return}let o=r.sessionName;if(!bt(o)){e.write(`HTTP/1.1 400 Bad Request\r
429
+ `),e.destroy();return}let o=r.sessionName;if(!yt(o)){e.write(`HTTP/1.1 400 Bad Request\r
430
430
  \r
431
- `),e.destroy();return}let{cols:s,rows:a}=r;FN.handleUpgrade(t,e,n,i=>up(i,o,{cols:s,rows:a}))};J0.on("upgrade",X0);var Y0=process.env.BOSTRAT_CERT_DIR||ft.join(process.env.HOME||of.tmpdir(),".bostrat","certs"),F0=Number(process.env.BOSTRAT_TLS_PORT)||3443,U0=ft.join(Y0,"bostrat.globopi.key"),H0=ft.join(Y0,"bostrat.globopi.crt");if(Gt.existsSync(U0)&&Gt.existsSync(H0)){let t=CN.createServer({key:Gt.readFileSync(U0),cert:Gt.readFileSync(H0)},x);t.on("upgrade",X0),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(F0,tc,()=>{console.log(`[bostrat-agent] https listening on ${tc||"0.0.0.0"}:${F0}`)})}J0.listen(j0,tc,()=>{console.log(`[bostrat-agent] listening on ${tc||"0.0.0.0"}:${j0}`),console.log(`[bostrat-agent] tmux socket: ${zt}`),console.log(`[bostrat-agent] auto-trigger default: ${q0}%`),console.log(`[bostrat-agent] ip allowlist: ${wo.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(sf.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Gb().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Vb().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Hb().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)),Nb().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)),qN(),WN(),dg({listTracks:at,getTrack:et}).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)),Ok()&&Mk({listTracks:at}),l0(),Dh({resolveTrack:et}),ak({listTracks:at,createTrack:yd,archiveTrack:so,listRepos:Re,resolveTrack:et,sayToLead:le,answerPrompt:hd,startLeadHandoff:xd,interruptLead:gd,listSubtaskJobs:ao,cancelSubtask:Td,readEvents:L,appendEvent:I,knownAgents:_i}),UN(),Ro().then(t=>{t||console.warn(`[boot] no host tmux server on ${zt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),v0(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(nc=fk({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||of.hostname(),name:process.env.BOSTRAT_NODE_NAME||of.hostname(),listTracks:at,detectAgents:pd,sampleMetrics:mk}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),hr().catch(()=>{}),ih().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Bh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),Mm().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),Xy().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),zh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{nc?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function UN(){HN(),Oa().catch(()=>{}),setInterval(()=>Oa().catch(()=>{}),1e4)}function HN(){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(W0,"..","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 WN(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await at(),r=await Ik({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 qN(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await at();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await md(n.id).catch(()=>{}),await Cy(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
431
+ `),e.destroy();return}let{cols:s,rows:a}=r;qN.handleUpgrade(t,e,n,i=>pp(i,o,{cols:s,rows:a}))};Z0.on("upgrade",Q0);var tx=process.env.BOSTRAT_CERT_DIR||ft.join(process.env.HOME||lf.tmpdir(),".bostrat","certs"),W0=Number(process.env.BOSTRAT_TLS_PORT)||3443,q0=ft.join(tx,"bostrat.globopi.key"),K0=ft.join(tx,"bostrat.globopi.crt");if(Kt.existsSync(q0)&&Kt.existsSync(K0)){let t=BN.createServer({key:Kt.readFileSync(q0),cert:Kt.readFileSync(K0)},x);t.on("upgrade",Q0),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(W0,sc,()=>{console.log(`[bostrat-agent] https listening on ${sc||"0.0.0.0"}:${W0}`)})}Z0.listen(U0,sc,()=>{console.log(`[bostrat-agent] listening on ${sc||"0.0.0.0"}:${U0}`),console.log(`[bostrat-agent] tmux socket: ${Vt}`),console.log(`[bostrat-agent] auto-trigger default: ${V0}%`),console.log(`[bostrat-agent] ip allowlist: ${ko.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(uf.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Xb().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Yb().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Gb().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)),jb().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)),zN(),VN(),hg({listTracks:ot,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)),Nk()&&Pk({listTracks:ot}),f0(),Fh({resolveTrack:Q}),uk({listTracks:ot,createTrack:Sd,archiveTrack:lo,listRepos:Ie,resolveTrack:Q,sayToLead:ce,answerPrompt:kd,startLeadHandoff:_d,interruptLead:vd,listSubtaskJobs:uo,cancelSubtask:Rd,readEvents:L,appendEvent:R,knownAgents:Ri}),KN(),Co().then(t=>{t||console.warn(`[boot] no host tmux server on ${Vt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),_0(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(ic=hk({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||lf.hostname(),name:process.env.BOSTRAT_NODE_NAME||lf.hostname(),listTracks:ot,detectAgents:yd,sampleMetrics:gk}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),yr().catch(()=>{}),dh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Uh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),Dm().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),tb().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),Zh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{ic?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function KN(){GN(),Na().catch(()=>{}),setInterval(()=>Na().catch(()=>{}),1e4)}function GN(){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(G0,"..","bin",t),n=ft.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Kt.existsSync(e))continue;Kt.mkdirSync(n,{recursive:!0});let r=ft.join(n,t);(Kt.existsSync(r)?Kt.readlinkSync(r):null)!==e&&(Kt.rmSync(r,{force:!0}),Kt.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 VN(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await ot(),r=await Ok({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 zN(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await ot();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await bd(n.id).catch(()=>{}),await By(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}