bostrat-node 0.2.13 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +44 -44
  2. package/dist/index.js +147 -146
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,121 +1,121 @@
1
- var jk=Object.defineProperty;var bd=(t,e)=>{for(var n in e)jk(t,n,{get:e[n],enumerable:!0})};import lt from"node:path";import jt from"node:fs";import xM from"node:readline";import{fileURLToPath as SM}from"node:url";import ve from"express";import vM from"node:http";import TM from"node:https";import gd from"node:os";import{WebSocketServer as AM}from"ws";import{execFile as qx}from"node:child_process";import{promisify as Kx}from"node:util";import Gi from"node:fs";import{randomUUID as Gx}from"node:crypto";import Lt from"node:fs/promises";import vd from"node:os";import Ot from"node:path";import{execFile as Wk}from"node:child_process";import{promisify as qk}from"node:util";import{createHash as Lk,randomBytes as Fk,timingSafeEqual as Uk}from"node:crypto";var Es="BOSTRAT_HOOK_TOKEN",_s="x-bostrat-hook";function kd(){return Fk(32).toString("hex")}function oo(t){let e=typeof t=="string"?t.trim():"";return e?Lk("sha256").update(e).digest("hex"):null}function xd(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:Uk(Buffer.from(t),Buffer.from(e))}var $i=new Map,Oi=new Map,Hk=6e4;function Is(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if($i.set(t,($i.get(t)||0)+1),Oi.has(t)&&n-Oi.get(t)<Hk)return!1;Oi.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=${$i.get(t)}${o?` ${o}`:""}`),!0}async function Sd(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 Rs(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Kk(){return process.env.CLAUDE_CONFIG_DIR?Ot.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Ot.join(vd.homedir(),".claude.json")}function Gk(){return process.env.CLAUDE_CONFIG_DIR?Ot.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Ot.join(vd.homedir(),".claude","settings.json")}async function Ge(t){if(!t||typeof t!="string")return;let e=Ot.resolve(t),n=Kk(),r=Gk(),o={};try{o=JSON.parse(await Lt.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 Lt.mkdir(Ot.dirname(n),{recursive:!0}),await Lt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Lt.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 "${_s}: $${Es}" --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 Lt.mkdir(Ot.dirname(r),{recursive:!0}),await Lt.writeFile(r,JSON.stringify(i,null,2)),await zk(e)}async function zk(t){try{let e=Ot.resolve(t),n=Ot.join(e,".bostrat","memory","claude"),r=Ot.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await Lt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Ot.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Lt.mkdir(n,{recursive:!0}),await Lt.mkdir(Ot.dirname(r),{recursive:!0}),await Lt.writeFile(r,JSON.stringify(o,null,2)),await Vk(e,["/.claude/settings.local.json"])}catch{}}async function Vk(t,e){try{let{stdout:n}=await qk(Wk)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Ot.resolve(t,n.trim()),o=await Lt.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
2
- `).includes(a));if(!s.length)return;await Lt.mkdir(Ot.dirname(r),{recursive:!0}),await Lt.appendFile(r,(o&&!o.endsWith(`
1
+ var Jk=Object.defineProperty;var Ed=(t,e)=>{for(var n in e)Jk(t,n,{get:e[n],enumerable:!0})};import ct from"node:path";import Lt from"node:fs";import PC from"node:readline";import{fileURLToPath as BC}from"node:url";import Se from"express";import DC from"node:http";import jC from"node:https";import vd from"node:os";import{WebSocketServer as LC}from"ws";import{execFile as m0}from"node:child_process";import{promisify as h0}from"node:util";import Yi from"node:fs";import{randomUUID as w0}from"node:crypto";import Ft from"node:fs/promises";import $d from"node:os";import Ct from"node:path";import{execFile as tx}from"node:child_process";import{promisify as ex}from"node:util";import{createHash as Xk,randomBytes as Yk,timingSafeEqual as Zk}from"node:crypto";var Ts="BOSTRAT_HOOK_TOKEN",As="x-bostrat-hook";function _d(){return Yk(32).toString("hex")}function no(t){let e=typeof t=="string"?t.trim():"";return e?Xk("sha256").update(e).digest("hex"):null}function Id(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:Zk(Buffer.from(t),Buffer.from(e))}var Oi=new Map,Ci=new Map,Qk=6e4;function Es(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(Oi.set(t,(Oi.get(t)||0)+1),Ci.has(t)&&n-Ci.get(t)<Qk)return!1;Ci.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=${Oi.get(t)}${o?` ${o}`:""}`),!0}async function Rd(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 _s(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function nx(){return process.env.CLAUDE_CONFIG_DIR?Ct.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Ct.join($d.homedir(),".claude.json")}function rx(){return process.env.CLAUDE_CONFIG_DIR?Ct.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Ct.join($d.homedir(),".claude","settings.json")}async function Ke(t){if(!t||typeof t!="string")return;let e=Ct.resolve(t),n=nx(),r=rx(),o={};try{o=JSON.parse(await Ft.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 Ft.mkdir(Ct.dirname(n),{recursive:!0}),await Ft.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Ft.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 "${As}: $${Ts}" --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 Ft.mkdir(Ct.dirname(r),{recursive:!0}),await Ft.writeFile(r,JSON.stringify(i,null,2)),await ox(e)}async function ox(t){try{let e=Ct.resolve(t),n=Ct.join(e,".bostrat","memory","claude"),r=Ct.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await Ft.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Ct.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Ft.mkdir(n,{recursive:!0}),await Ft.mkdir(Ct.dirname(r),{recursive:!0}),await Ft.writeFile(r,JSON.stringify(o,null,2)),await sx(e,["/.claude/settings.local.json"])}catch{}}async function sx(t,e){try{let{stdout:n}=await ex(tx)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Ct.resolve(t,n.trim()),o=await Ft.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
2
+ `).includes(a));if(!s.length)return;await Ft.mkdir(Ct.dirname(r),{recursive:!0}),await Ft.appendFile(r,(o&&!o.endsWith(`
3
3
  `)?`
4
4
  `:"")+s.join(`
5
5
  `)+`
6
- `)}catch{}}import Mi from"node:fs/promises";import Jk from"node:os";import $s from"node:path";function Xk(){let t=process.env.CODEX_HOME||$s.join(Jk.homedir(),".codex");return $s.join(t,"config.toml")}function Td(t){return`[projects."${t}"]`}function Yk(t,e){let n=Td(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 tr(t){if(!t||typeof t!="string")return;let e=$s.resolve(t),n=Xk(),r="";try{r=await Mi.readFile(n,"utf8")}catch{}if(Yk(r,e))return;let o=Td(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 Mi from"node:fs/promises";import ax from"node:os";import Is from"node:path";function ix(){let t=process.env.CODEX_HOME||Is.join(ax.homedir(),".codex");return Is.join(t,"config.toml")}function Od(t){return`[projects."${t}"]`}function cx(t,e){let n=Od(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 er(t){if(!t||typeof t!="string")return;let e=Is.resolve(t),n=ix(),r="";try{r=await Mi.readFile(n,"utf8")}catch{}if(cx(r,e))return;let o=Od(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 Mi.mkdir($s.dirname(n),{recursive:!0}),await Mi.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Ad from"node:fs/promises";import Zk from"node:os";import so from"node:path";function Qk(t){let n=so.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||so.join(Zk.homedir(),".cursor");return so.join(r,"projects",n)}async function er(t){if(!t||typeof t!="string")return;let e=so.resolve(t),n=Qk(e),r=so.join(n,".workspace-trusted");await Ad.mkdir(n,{recursive:!0}),await Ad.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
- `,"utf8")}import j from"node:fs/promises";import Ex from"node:os";import J from"node:path";import{execFile as _x}from"node:child_process";import{promisify as Ix}from"node:util";import ux from"node:fs";import _d from"node:fs/promises";import io from"node:path";import dx from"node:os";import{fileURLToPath as fx}from"node:url";import{execFile as px}from"node:child_process";import{promisify as mx}from"node:util";import Ni from"node:fs/promises";import ze from"node:path";import{execFile as tx}from"node:child_process";import{promisify as ex}from"node:util";var nx=ex(tx),Ed=process.env.HOME||"/home/globo",rx=ze.join(process.env.BOSTRAT_STATE_DIR||ze.join(Ed,".bostrat"),"repos"),ox=[...(process.env.BOSTRAT_REPOS_ROOT||ze.join(Ed,"Documents")).split(","),rx].map(t=>t.trim()).filter(Boolean),sx=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,ax=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function ix(t){try{return(await Ni.stat(ze.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function cx(t){let e=i=>Ni.readFile(ze.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 Te(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await ix(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:ze.basename(o),branch:await cx(o)});return}if(s>=sx)return;let i;try{i=await Ni.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||ax.has(c.name)||await e(ze.join(o,c.name),s+1)}for(let o of ox)await e(ze.resolve(o),0);let n=new Set,r=[];for(let o of t.values()){let s=(await tt(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 lx(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var Ci=new Map;async function tt(t){if(!t)return null;if(Ci.has(t))return Ci.get(t);let e=null;try{let{stdout:n}=await nx("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=lx(n)}catch{e=null}return Ci.set(t,e),e}async function ao(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await Te())if((await tt(n.path))?.toLowerCase()===e)return n.path;return null}var Id=mx(px),hx=process.env.BOSTRAT_STATE_DIR||io.join(process.env.HOME||dx.tmpdir(),".bostrat"),wx=io.join(hx,"repos"),Rd=/^[^/\s]+\/[^/\s]+$/,gx=5*6e4,yx=6e4,bx=10*6e4,$d=fetch,Pi=new Map,Tn=null;function Gt(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 Os(t=process.env){return!!(Gt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Od=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),nr=(t,e)=>Object.assign(new Error(t),{status:e});async function Ms(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Rd.test(n))throw nr("repo must be owner/name",400);let r=Pi.get(n);if(r&&r.expiresAt-Date.now()>gx)return r;if(!Os(e))throw nr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await $d(`${Gt(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Od(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw nr(`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 nr(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 Pi.set(n,a),a}async function co({env:t=process.env,force:e=!1}={}){if(!Os(t))return{configured:!1,repos:[],username:null};if(!e&&Tn&&Date.now()-Tn.at<yx)return Tn.data;try{let n=await $d(`${Gt(t)}/v1/github/repos`,{headers:Od(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Tn={at:Date.now(),data:r},r}catch(n){return Tn?Tn.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Md(){Pi.clear(),Tn=null}async function ae({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await tt(t):null);if(!r)return{};let{token:o}=await Ms(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Bi({env:t=process.env}={}){return(await co({env:t})).username??null}var kx=()=>fx(new URL("../bin/bostrat-git-credential",import.meta.url)),xx=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Sx(t){let[e,n]=String(t).split("/");return io.join(wx,e,n)}async function Di(t,{env:e=process.env}={}){let n=(...r)=>Id("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${kx()}" ${xx(e)}`),await n("config","credential.useHttpPath","true")}async function ji(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Rd.test(String(t||"")))throw nr(`bad repo slug "${t}"`,400);let r=Sx(t);if(ux.existsSync(io.join(r,".git")))return r;let{token:o}=await Ms(t,{env:e});await _d.mkdir(io.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Id("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:bx,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await _d.rm(r,{recursive:!0,force:!0}).catch(()=>{}),nr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Di(r,{env:e}),r}var vx=6e4,Tx=5e3,Li=new Map,Ax=(...t)=>fetch(...t);function Cd(t=process.env){return!!(Gt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Nd=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Pd(t,e,n){let r=Gt(n),o=new AbortController,s=setTimeout(()=>o.abort(),Tx);try{return await Ax(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function Bd(t,{env:e=process.env,force:n=!1}={}){if(!Cd(e))return{facts:[],source:"unconfigured"};let r=Li.get(t);if(!n&&r&&Date.now()-r.at<vx)return{facts:r.facts,source:"cache"};try{let o=await Pd(`/v1/memory/${encodeURIComponent(t)}`,{headers:Nd(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return Li.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 Cs(t,e,{env:n=process.env}={}){if(!Cd(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await Pd(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Nd(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 Li.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 Rx=Ix(_x),Fi=process.env.BOSTRAT_STATE_DIR||J.join(process.env.HOME||Ex.tmpdir(),".bostrat"),Dd=200,$x=25*1024;function Ns(){return J.join(Fi,"memory","repos")}var Ae="operator";function An(t){return t===Ae?J.join(Fi,"memory",Ae):J.join(Ns(),t)}var Ud=".tombstones.json";async function lo(t){try{return JSON.parse(await j.readFile(J.join(t,Ud),"utf8"))}catch{return{}}}async function Ui(t,e){let n=await lo(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(J.join(t,Ud),JSON.stringify(n))}function jd(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 Ld(t){try{let{stdout:e}=await Rx("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function Hi(t){if(!t)return[];let e=jd(await Ld(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=jd(await Ld(J.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Ps(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 Ve(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function ie(t,e=null){let n=e??await j.readFile(t,"utf8").catch(()=>""),{fm:r}=Ps(n),o=Ve(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 Hd(t,e){let n=Ve(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Ae){let o=Ve(t,"trust");if(!o||/^operator/i.test(o))return Ae}return e}var rr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function Wi(t,e,n,r=new Map){let o=(t||"").split(`
13
+ `:"")}try{await Mi.mkdir(Is.dirname(n),{recursive:!0}),await Mi.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Cd from"node:fs/promises";import lx from"node:os";import ro from"node:path";function ux(t){let n=ro.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||ro.join(lx.homedir(),".cursor");return ro.join(r,"projects",n)}async function nr(t){if(!t||typeof t!="string")return;let e=ro.resolve(t),n=ux(e),r=ro.join(n,".workspace-trusted");await Cd.mkdir(n,{recursive:!0}),await Cd.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
+ `,"utf8")}import B from"node:fs/promises";import Dx from"node:os";import X from"node:path";import{execFile as jx}from"node:child_process";import{promisify as Lx}from"node:util";import xx from"node:fs";import Nd from"node:fs/promises";import so from"node:path";import Sx from"node:os";import{fileURLToPath as vx}from"node:url";import{execFile as Tx}from"node:child_process";import{promisify as Ax}from"node:util";import Pi from"node:fs/promises";import Ge from"node:path";import{execFile as dx}from"node:child_process";import{promisify as fx}from"node:util";var px=fx(dx),Md=process.env.HOME||"/home/globo",mx=Ge.join(process.env.BOSTRAT_STATE_DIR||Ge.join(Md,".bostrat"),"repos"),hx=[...(process.env.BOSTRAT_REPOS_ROOT||Ge.join(Md,"Documents")).split(","),mx].map(t=>t.trim()).filter(Boolean),wx=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,gx=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function yx(t){try{return(await Pi.stat(Ge.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function bx(t){let e=i=>Pi.readFile(Ge.join(t,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await e("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await e(`refs/heads/${i}`)||(o??=await e("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await e("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function ve(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await yx(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:Ge.basename(o),branch:await bx(o)});return}if(s>=wx)return;let i;try{i=await Pi.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||gx.has(c.name)||await e(Ge.join(o,c.name),s+1)}for(let o of hx)await e(Ge.resolve(o),0);let n=new Set,r=[];for(let o of t.values()){let s=(await tt(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 kx(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var Ni=new Map;async function tt(t){if(!t)return null;if(Ni.has(t))return Ni.get(t);let e=null;try{let{stdout:n}=await px("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=kx(n)}catch{e=null}return Ni.set(t,e),e}async function oo(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await ve())if((await tt(n.path))?.toLowerCase()===e)return n.path;return null}var Pd=Ax(Tx),Ex=process.env.BOSTRAT_STATE_DIR||so.join(process.env.HOME||Sx.tmpdir(),".bostrat"),_x=so.join(Ex,"repos"),Bd=/^[^/\s]+\/[^/\s]+$/,Ix=5*6e4,Rx=6e4,$x=10*6e4,Dd=fetch,Bi=new Map,An=null;function zt(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 Rs(t=process.env){return!!(zt(t)&&t.BOSTRAT_PLANE_TOKEN)}var jd=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),rr=(t,e)=>Object.assign(new Error(t),{status:e});async function $s(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Bd.test(n))throw rr("repo must be owner/name",400);let r=Bi.get(n);if(r&&r.expiresAt-Date.now()>Ix)return r;if(!Rs(e))throw rr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Dd(`${zt(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:jd(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw rr(`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 rr(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 Bi.set(n,a),a}async function ao({env:t=process.env,force:e=!1}={}){if(!Rs(t))return{configured:!1,repos:[],username:null};if(!e&&An&&Date.now()-An.at<Rx)return An.data;try{let n=await Dd(`${zt(t)}/v1/github/repos`,{headers:jd(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return An={at:Date.now(),data:r},r}catch(n){return An?An.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Ld(){Bi.clear(),An=null}async function ae({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await tt(t):null);if(!r)return{};let{token:o}=await $s(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Di({env:t=process.env}={}){return(await ao({env:t})).username??null}var Ox=()=>vx(new URL("../bin/bostrat-git-credential",import.meta.url)),Cx=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Mx(t){let[e,n]=String(t).split("/");return so.join(_x,e,n)}async function ji(t,{env:e=process.env}={}){let n=(...r)=>Pd("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Ox()}" ${Cx(e)}`),await n("config","credential.useHttpPath","true")}async function Li(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Bd.test(String(t||"")))throw rr(`bad repo slug "${t}"`,400);let r=Mx(t);if(xx.existsSync(so.join(r,".git")))return r;let{token:o}=await $s(t,{env:e});await Nd.mkdir(so.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Pd("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:$x,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Nd.rm(r,{recursive:!0,force:!0}).catch(()=>{}),rr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await ji(r,{env:e}),r}var Nx=6e4,Px=5e3,Fi=new Map,Bx=(...t)=>fetch(...t);function Fd(t=process.env){return!!(zt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Ud=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Hd(t,e,n){let r=zt(n),o=new AbortController,s=setTimeout(()=>o.abort(),Px);try{return await Bx(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function Wd(t,{env:e=process.env,force:n=!1}={}){if(!Fd(e))return{facts:[],source:"unconfigured"};let r=Fi.get(t);if(!n&&r&&Date.now()-r.at<Nx)return{facts:r.facts,source:"cache"};try{let o=await Hd(`/v1/memory/${encodeURIComponent(t)}`,{headers:Ud(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return Fi.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 Os(t,e,{env:n=process.env}={}){if(!Fd(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await Hd(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Ud(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 Fi.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 Fx=Lx(jx),Ui=process.env.BOSTRAT_STATE_DIR||X.join(process.env.HOME||Dx.tmpdir(),".bostrat"),qd=200,Ux=25*1024;function Cs(){return X.join(Ui,"memory","repos")}var Te="operator";function En(t){return t===Te?X.join(Ui,"memory",Te):X.join(Cs(),t)}var zd=".tombstones.json";async function io(t){try{return JSON.parse(await B.readFile(X.join(t,zd),"utf8"))}catch{return{}}}async function Hi(t,e){let n=await io(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await B.mkdir(t,{recursive:!0}),await B.writeFile(X.join(t,zd),JSON.stringify(n))}function Kd(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 Gd(t){try{let{stdout:e}=await Fx("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function Wi(t){if(!t)return[];let e=Kd(await Gd(t));if(e)return[e];let n=[],r=[];try{r=await B.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Kd(await Gd(X.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Ms(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 Ve(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function ie(t,e=null){let n=e??await B.readFile(t,"utf8").catch(()=>""),{fm:r}=Ms(n),o=Ve(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await B.stat(t)).mtimeMs}catch{return 0}}function Jd(t,e){let n=Ve(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Te){let o=Ve(t,"trust");if(!o||/^operator/i.test(o))return Te}return e}var or=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function qi(t,e,n,r=new Map){let o=(t||"").split(`
15
15
  `),s=new Map;for(let c of(e||"").split(`
16
- `)){let l=rr(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=rr(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=or(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=or(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 Wd(t,e){let n=Ve(e,"name")||t.replace(/\.md$/,""),r=Ve(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function uo(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 qd(t,e){let n=J.join(t,".bostrat","memory"),r=await uo(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=J.join(n,l),f;try{f=await j.readFile(u,"utf8")}catch{continue}let{fm:d}=Ps(f),m=Hd(d,e);if(!m)continue;let h=An(m),w=J.join(h,l),g=await ie(u,f);if(i.has(h)||i.set(h,await lo(h)),(i.get(h)[l]||0)>=g)continue;let b=await ie(w).catch(()=>0);if(await j.access(w).then(()=>!0).catch(()=>!1)&&b>=g)continue;await j.mkdir(h,{recursive:!0}),await j.writeFile(w,f),a++;let A=o.get(h);A||o.set(h,A={won:new Set,generated:new Map}),A.won.add(l),A.generated.set(l,Wd(l,d)),s.has(m)||s.set(m,new Set),s.get(m).add(l)}let c=await j.readFile(J.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=J.join(l,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,Wi(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Ox(){let t=J.join(Fi,"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=J.join(t,o.name);if(!await j.access(J.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await Hi(s),c=await qd(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 Mx=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,Cx=t=>{let e=new Map;for(let n of(t||"").split(`
20
- `)){let r=rr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function Nx(t,e){let n=An(t),r=await lo(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!Mx.test(l)||l==="MEMORY.md")continue;let u=J.join(n,l),f=await j.access(u).then(()=>!0).catch(()=>!1),d=f?await ie(u):0,m=Number(c.modified)||0;if(c.deleted){m>d&&(f&&await j.rm(u,{force:!0}),await Ui(n,[[l,m]]),f&&(a.push(l),i++));continue}f&&d>=m||(r[l]||0)>=m||(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=J.join(n,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=Wi(l,[...s.values()].join(`
19
+ `)}function Xd(t,e){let n=Ve(e,"name")||t.replace(/\.md$/,""),r=Ve(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function co(t){let e=[];try{e=await B.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 Yd(t,e){let n=X.join(t,".bostrat","memory"),r=await co(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 B.readFile(u,"utf8")}catch{continue}let{fm:d}=Ms(f),p=Jd(d,e);if(!p)continue;let m=En(p),g=X.join(m,l),y=await ie(u,f);if(i.has(m)||i.set(m,await io(m)),(i.get(m)[l]||0)>=y)continue;let b=await ie(g).catch(()=>0);if(await B.access(g).then(()=>!0).catch(()=>!1)&&b>=y)continue;await B.mkdir(m,{recursive:!0}),await B.writeFile(g,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,Xd(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await B.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=X.join(l,"MEMORY.md"),d=await B.readFile(f,"utf8").catch(()=>"");await B.writeFile(f,qi(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Hx(){let t=X.join(Ui,"workspaces"),e=[];try{e=await B.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 B.access(X.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await Wi(s),c=await Yd(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 Wx=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,qx=t=>{let e=new Map;for(let n of(t||"").split(`
20
+ `)){let r=or(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function Kx(t,e){let n=En(t),r=await io(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!Wx.test(l)||l==="MEMORY.md")continue;let u=X.join(n,l),f=await B.access(u).then(()=>!0).catch(()=>!1),d=f?await ie(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await B.rm(u,{force:!0}),await Hi(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await B.mkdir(n,{recursive:!0}),await B.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 B.readFile(c,"utf8").catch(()=>""),u=qi(l,[...s.values()].join(`
21
21
  `),o);if(a.length){let f=new Set(a);u=u.split(`
22
- `).filter(d=>!f.has(rr(d))).join(`
23
- `)}await j.mkdir(n,{recursive:!0}),await j.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function qi(t,e=null){let n=An(t),r=e?.length?e:await uo(n);if(!r.length)return{pushed:0,source:"empty"};let o=Cx(await j.readFile(J.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=J.join(n,a),c;try{c=await j.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await ie(i,c),indexLine:o.get(a)??null})}return Cs(t,s)}async function Px(t){let{facts:e,source:n}=await Bd(t);e.length?await Nx(t,e):n==="plane"&&await qi(t)}function Bx(t){let e=t.split(`
24
- `);e.length>Dd&&(e=e.slice(0,Dd));let n=e.join(`
25
- `);for(;Buffer.byteLength(n,"utf8")>$x&&e.length;)e.pop(),n=e.join(`
26
- `);return n}var Dx=`# Track memory
22
+ `).filter(d=>!f.has(or(d))).join(`
23
+ `)}await B.mkdir(n,{recursive:!0}),await B.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Ki(t,e=null){let n=En(t),r=e?.length?e:await co(n);if(!r.length)return{pushed:0,source:"empty"};let o=qx(await B.readFile(X.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=X.join(n,a),c;try{c=await B.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await ie(i,c),indexLine:o.get(a)??null})}return Os(t,s)}async function Gx(t){let{facts:e,source:n}=await Wd(t);e.length?await Kx(t,e):n==="plane"&&await Ki(t)}function Vx(t){let e=t.split(`
24
+ `);e.length>qd&&(e=e.slice(0,qd));let n=e.join(`
25
+ `);for(;Buffer.byteLength(n,"utf8")>Ux&&e.length;)e.pop(),n=e.join(`
26
+ `);return n}var zx=`# 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 jx(t,e){if(!t||!e?.length)return{materialized:!1,reason:"no-repo-key"};let n=J.join(t,".bostrat","memory"),r=0,o=0,s=[];for(let u of e){let f=An(u),d=await lo(f);for(let[g,b]of Object.entries(d)){let S=J.join(n,g);await j.access(S).then(()=>!0).catch(()=>!1)&&await ie(S)<=b&&(await j.rm(S,{force:!0}),o++)}let m=await uo(f);if(!m.length)continue;await j.mkdir(n,{recursive:!0});for(let g of m){let b=J.join(f,g),S=J.join(n,g),A=await ie(b),C=await ie(S).catch(()=>0);await j.access(S).then(()=>!0).catch(()=>!1)&&C>=A||(await j.copyFile(b,S),r++)}let h=await j.readFile(J.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===Ae?"## operator memory":`## repo: ${u}`;h.trim()&&s.push(e.length>1?`${w}
31
+ `;async function Jx(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=En(u),d=await io(f);for(let[y,b]of Object.entries(d)){let S=X.join(n,y);await B.access(S).then(()=>!0).catch(()=>!1)&&await ie(S)<=b&&(await B.rm(S,{force:!0}),o++)}let p=await co(f);if(!p.length)continue;await B.mkdir(n,{recursive:!0});for(let y of p){let b=X.join(f,y),S=X.join(n,y),E=await ie(b),M=await ie(S).catch(()=>0);await B.access(S).then(()=>!0).catch(()=>!1)&&M>=E||(await B.copyFile(b,S),r++)}let m=await B.readFile(X.join(f,"MEMORY.md"),"utf8").catch(()=>""),g=u===Te?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${g}
32
32
 
33
- ${h.trim()}`:h.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(`
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=Bx(i);await j.mkdir(n,{recursive:!0}),await j.writeFile(J.join(n,"MEMORY.md"),c);let l=J.join(n,"claude");return await j.mkdir(l,{recursive:!0}),await j.writeFile(J.join(l,"MEMORY.md"),Dx,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var Fd=0,Lx=3e4;async function Kd(t){try{let e=await Hi(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Ae];if(Date.now()-Fd>Lx){Fd=Date.now(),await Fx();let{changedByKey:r}=await Ox();for(let[o,s]of r)await qi(o,[...s])}for(let r of n)await Px(r);return await jx(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function Fx(){let t=0,e=[];try{e=await j.readdir(Ns(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=J.join(Ns(),n.name),o=An(Ae),s=new Set,a=new Map;for(let m of await uo(r)){let h=J.join(r,m),w;try{w=await j.readFile(h,"utf8")}catch{continue}let{fm:g}=Ps(w);if(Hd(g,n.name)!==Ae)continue;let b=J.join(o,m),S=await ie(b).catch(()=>0),A=await ie(h,w);await j.mkdir(o,{recursive:!0}),A>S&&await j.writeFile(b,w),await j.rm(h,{force:!0}),s.add(m),a.set(m,Wd(m,g)),t++}if(!s.size)continue;let i=Date.now();await Ui(r,[...s].map(m=>[m,i])),await Cs(n.name,[...s].map(m=>({file:m,deleted:!0,modified:i})));let c=J.join(r,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=l.split(`
37
- `).filter(m=>!s.has(rr(m))).join(`
38
- `);await j.writeFile(c,u).catch(()=>{});let f=J.join(o,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,Wi(d,l,s,a))}return{moved:t}}async function Gd(){let t=[],e=[Ae];try{for(let n of await j.readdir(Ns(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=An(n),o=await uo(r),s=await lo(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await j.readFile(J.join(r,i),"utf8").catch(()=>""),{fm:l}=Ps(c);a.push({file:i,name:Ve(l,"name")||i.replace(/\.md$/,""),description:Ve(l,"description")||"",scope:Ve(l,"scope")||null,modified:await ie(J.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function zd(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=An(t),r=J.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 Ui(n,[[e,s]]);let a=J.join(n,"MEMORY.md"),i=await j.readFile(a,"utf8").catch(()=>"");await j.writeFile(a,i.split(`
39
- `).filter(l=>rr(l)!==e).join(`
40
- `)).catch(()=>{});let c=await Cs(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function Vd(t){try{let e=await Hi(t),{harvested:n,changedByKey:r}=await qd(t,e[0]||null);for(let[o,s]of r)await qi(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import fo from"node:fs/promises";import Ux from"node:os";import Xd from"node:path";var Hx=[/[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 po(t){let e=String(t??"");for(let n of Hx)e=e.replace(n,"\u2039redacted\u203A");return e}var Ki=process.env.BOSTRAT_DEBUG_DIR||Xd.join(Ux.homedir(),".bostrat","debug"),or=Xd.join(Ki,"handoff.jsonl"),Jd=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function Bs(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function Wx(t){try{await fo.mkdir(Ki,{recursive:!0}),await fo.appendFile(or,`${JSON.stringify(t)}
41
- `,"utf8");let n=(await fo.readFile(or,"utf8")).split(`
42
- `).filter(Boolean);n.length>Jd&&await fo.writeFile(or,`${n.slice(-Jd).join(`
36
+ `,c=Vx(i);await B.mkdir(n,{recursive:!0}),await B.writeFile(X.join(n,"MEMORY.md"),c);let l=X.join(n,"claude");return await B.mkdir(l,{recursive:!0}),await B.writeFile(X.join(l,"MEMORY.md"),zx,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var Vd=0,Xx=3e4;async function Zd(t){try{let e=await Wi(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Te];if(Date.now()-Vd>Xx){Vd=Date.now(),await Yx();let{changedByKey:r}=await Hx();for(let[o,s]of r)await Ki(o,[...s])}for(let r of n)await Gx(r);return await Jx(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function Yx(){let t=0,e=[];try{e=await B.readdir(Cs(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=X.join(Cs(),n.name),o=En(Te),s=new Set,a=new Map;for(let p of await co(r)){let m=X.join(r,p),g;try{g=await B.readFile(m,"utf8")}catch{continue}let{fm:y}=Ms(g);if(Jd(y,n.name)!==Te)continue;let b=X.join(o,p),S=await ie(b).catch(()=>0),E=await ie(m,g);await B.mkdir(o,{recursive:!0}),E>S&&await B.writeFile(b,g),await B.rm(m,{force:!0}),s.add(p),a.set(p,Xd(p,y)),t++}if(!s.size)continue;let i=Date.now();await Hi(r,[...s].map(p=>[p,i])),await Os(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=X.join(r,"MEMORY.md"),l=await B.readFile(c,"utf8").catch(()=>""),u=l.split(`
37
+ `).filter(p=>!s.has(or(p))).join(`
38
+ `);await B.writeFile(c,u).catch(()=>{});let f=X.join(o,"MEMORY.md"),d=await B.readFile(f,"utf8").catch(()=>"");await B.writeFile(f,qi(d,l,s,a))}return{moved:t}}async function Qd(){let t=[],e=[Te];try{for(let n of await B.readdir(Cs(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=En(n),o=await co(r),s=await io(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await B.readFile(X.join(r,i),"utf8").catch(()=>""),{fm:l}=Ms(c);a.push({file:i,name:Ve(l,"name")||i.replace(/\.md$/,""),description:Ve(l,"description")||"",scope:Ve(l,"scope")||null,modified:await ie(X.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function tf(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=En(t),r=X.join(n,e);if(!await B.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await B.rm(r,{force:!0}),await Hi(n,[[e,s]]);let a=X.join(n,"MEMORY.md"),i=await B.readFile(a,"utf8").catch(()=>"");await B.writeFile(a,i.split(`
39
+ `).filter(l=>or(l)!==e).join(`
40
+ `)).catch(()=>{});let c=await Os(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function ef(t){try{let e=await Wi(t),{harvested:n,changedByKey:r}=await Yd(t,e[0]||null);for(let[o,s]of r)await Ki(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import Ji from"node:fs/promises";import df from"node:path";import of from"node:fs/promises";import sf from"node:os";import af from"node:path";import Gi from"node:fs/promises";import Zx from"node:path";var Qx=0;async function Mt(t,e,{mode:n}={}){await Gi.mkdir(Zx.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++Qx}.tmp`;await Gi.writeFile(r,JSON.stringify(e,null,2)+`
41
+ `,n!=null?{mode:n}:void 0),await Gi.rename(r,t)}var t0=1e4,e0=3e4,nf=2e3,n0=()=>process.env.BOSTRAT_STATE_DIR||af.join(process.env.HOME||sf.tmpdir(),".bostrat"),cf=()=>af.join(n0(),"content-outbox.json"),r0=()=>process.env.BOSTRAT_NODE_ID||sf.hostname(),o0=(...t)=>fetch(...t),s0=e0;function ft(t=process.env){return!!(zt(t)&&t.BOSTRAT_PLANE_TOKEN)}var uo=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function fo(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),t0);try{return await o0(`${zt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var Ps=null,Vi=null,Ns=null,lo=null;async function lf(){return Ps||(Vi||=of.readFile(cf(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>Ps={ops:Array.isArray(t?.ops)?t.ops:[]}).finally(()=>Vi=null),Vi)}async function uf(){await Mt(cf(),Ps).catch(()=>{})}function rf(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function zi(t){let e=await lf(),n=rf(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=>rf(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>nf&&(e.ops=e.ops.slice(-nf)),await uf()}function a0(){Ns||(Ns=setTimeout(()=>{Ns=null,sr().catch(()=>{})},s0),Ns.unref?.())}async function i0(t,e=process.env){if(t.op==="record"){let n=await fo(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...uo(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:r0()}]})},e);if(!n.ok)throw new Error(`plane ${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 of.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await fo(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...uo(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw new Error(`plane ${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 fo(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:uo(e)},e);if(!n.ok)throw new Error(`plane ${n.status}`);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function sr({env:t=process.env,log:e=console}={}){return ft(t)?lo||(lo=(async()=>{let n=await lf(),r=0;for(;n.ops.length;){let o=n.ops[0],s;try{s=await i0(o,t)}catch(a){return a0(),{flushed:r,pending:n.ops.length,error:a?.message||String(a)}}s.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${o.op} ${o.family}/${o.id}: ${s.rejected}`),n.ops.shift(),r++,await uf()}return{flushed:r,pending:0}})().finally(()=>{lo=null}),lo):{flushed:0,source:"unconfigured"}}async function Rt(t,e,{trackId:n=null,env:r=process.env}={}){return ft(r)?(await zi({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),sr({env:r})):{queued:!1,source:"unconfigured"}}async function ze(t,e,n,r,o,{env:s=process.env}={}){return ft(s)?(await zi({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),sr({env:s})):{queued:!1,source:"unconfigured"}}async function Qt(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return ft(r)?(await zi({op:"delete",family:t,id:e,deletedAt:n}),sr({env:r})):{queued:!1,source:"unconfigured"}}async function vt(t,{env:e=process.env}={}){if(!ft(e))return{records:[],source:"unconfigured"};try{let n=await fo(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:uo(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 Bs(t,e,n,r,{env:o=process.env}={}){if(!ft(o))return null;try{let s=await fo(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:uo(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 c0=".bostrat/handoff",l0="latest.md",u0=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null;async function ff(t,e,{box:n=null,agent:r=null}={}){if(!t||!ft())return{pushed:!1};try{let o=Date.now();return await Rt("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await ze("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function pf(t){try{let e=u0(t);if(!e||!ft())return{restored:!1};let n=df.join(t,c0,l0);if(await Ji.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await vt("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 Bs("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Ji.mkdir(df.dirname(n),{recursive:!0}),await Ji.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import po from"node:fs/promises";import d0 from"node:os";import hf from"node:path";var f0=[/[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 f0)e=e.replace(n,"\u2039redacted\u203A");return e}var Xi=process.env.BOSTRAT_DEBUG_DIR||hf.join(d0.homedir(),".bostrat","debug"),ar=hf.join(Xi,"handoff.jsonl"),mf=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function Ds(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function p0(t){try{await po.mkdir(Xi,{recursive:!0}),await po.appendFile(ar,`${JSON.stringify(t)}
42
+ `,"utf8");let n=(await po.readFile(ar,"utf8")).split(`
43
+ `).filter(Boolean);n.length>mf&&await po.writeFile(ar,`${n.slice(-mf).join(`
43
44
  `)}
44
- `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${or}: ${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&&Bs())try{e.paneTail=po((await gt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(Bs()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed)/.test(String(e.phase||e.step||"")))&&await Wx(e)}async function Yd({limit:t=80}={}){try{return(await fo.readFile(or,"utf8")).split(`
45
- `).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=po(o.paneTail)),o}catch{return{raw:po(r)}}})}catch{return[]}}var Zd={DEBUG_DIR:Ki,LOG_FILE:or};var ut=Kx(qx);function mo(t){return new Promise(e=>setTimeout(e,t))}function Ds(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 N=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux",Ft=process.env.BOSTRAT_TMUX_SOCKET||"/tmp/tmux-1000/default",zx=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",zi=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Vi="bostrat-usage-probe",Vx=new Set([Vi]);function Ji(t,e){let n=sr(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function sr(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function ar(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}async function Xi(t,{loginShell:e=!1,failOpen:n=!1}={}){if(!t)return!1;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await ut(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function js(t){return Xi(String(t||"").trim().split(/\s+/)[0])}var Yi={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 B(...t){return["-S",Ft,...t]}async function Je(){if(!Gi.existsSync(Ft))return!1;try{return await ut(N,B("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function ho(){if(await Je())return!0;try{Gi.mkdirSync(Ft.replace(/\/[^/]+$/,""),{recursive:!0}),await ut(N,B("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Je()}var Jx=/^[A-Za-z0-9_.-]{1,64}$/;function yt(t){return typeof t=="string"&&Jx.test(t)}async function En(){if(!Gi.existsSync(Ft))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 ut(N,B("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
46
- `).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d]=r.split("|"),m=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(m)?n-m*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null)}}).filter(r=>!Vx.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function Y(t){return(await En()).find(n=>n.name===t)||null}var Xx=/^[A-Za-z0-9 ._↻-]+$/,Yx=64;function Zx(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=Yx&&Xx.test(e)}function Zi(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Qx(t){try{let{stdout:e}=await ut(N,B("list-sessions","-F","#{session_name}"));return e.split(`
47
- `).map(n=>n.trim()).includes(t)}catch{return!1}}async function _n(t,e=zx,n={}){if(!Zx(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Zi(r);if(!yt(o))throw new Error("resulting tmux session name is invalid");if(!await ho())throw new Error(`host tmux server not running on ${Ft} and auto-start failed \u2014 start one: ${N} -S ${Ft} new-session -d -s keeper "sleep 2147483647"`);if(await Qx(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||zi,a=n.agent||ar(s),i=!!n.resumeSessionId,c=n.resumeSessionId||Gx(),l=i?s:Ji(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&sr(s),d=n.firstRun===!0;d||(f&&await Ge(e),a==="codex"&&await tr(e),a==="cursor"&&await er(e),await Kd(e));let m=f&&!d?Rs():null,h=kd(),w=["-e",`${Es}=${h}`];m&&w.push("-e",`ANTHROPIC_API_KEY=${m}`);try{await ut(N,B("new-session","-d","-s",o,"-c",e,...w))}catch(g){throw Ds(m?Ds(g,m):g,h)}return await In(o),await ut(N,B("set-option","-t",o,"@bostrat-session","1")),await ut(N,B("set-option","-t",o,"@bostrat-title",r)),await ut(N,B("set-option","-t",o,"@bostrat-sid",c)),await ut(N,B("set-option","-t",o,"@bostrat-agent",a)),await ut(N,B("send-keys","-t",o,l,"Enter")),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:h}}async function ce(t){if(!yt(t))throw new Error("invalid session name");await ut(N,B("kill-session","-t",t))}async function Qd(t,e){if(yt(t))try{let n=e?B("set-option","-t",t,"@bostrat-sid",e):B("set-option","-u","-t",t,"@bostrat-sid");await ut(N,n)}catch{}}async function gt(t,{lines:e=40}={}){if(!yt(t))return"";try{let n=B("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await ut(N,n);return String(r||"")}catch{return""}}var t0=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,e0=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,At=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,n0=Number(process.env.BOSTRAT_BOX_COLS)||120,r0=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function tf(t){if(!yt(t))return 0;try{let{stdout:e}=await ut(N,B("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
48
- `).filter(Boolean).length}catch{return 0}}async function In(t,{cols:e=n0,rows:n=r0}={}){if(yt(t)&&!await tf(t))try{let r=await nf(t);if(r.cols===e&&r.rows===n)return;await ut(N,B("resize-window","-t",t,"-x",String(e),"-y",String(n))),await ut(N,B("set-window-option","-t",t,"window-size","latest")),await mo(80)}catch{}}async function ef(t){if(!yt(t))return 0;try{let{stdout:e}=await ut(N,B("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function nf(t){if(!yt(t))return{cols:0,rows:0};try{let{stdout:e}=await ut(N,B("display-message","-t",t,"-p","#{window_width} #{window_height}")),[n,r]=String(e||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function Ls(t,{cols:e=t0,rows:n=e0}={}){if(yt(t)&&!await tf(t))try{let r=await nf(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await ut(N,B("resize-window","-t",t,"-x",String(o),"-y",String(s))),await ut(N,B("set-window-option","-t",t,"window-size","latest")),await mo(80))}catch{}}async function Xe(t){return await Ls(t),gt(t,{lines:At})}var o0=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function xt(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=>o0.has(s));for(let s=0;s<o.length;s++)await ut(N,B("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await mo(n);return G({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Fs(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 ut(N,B("send-keys","-t",t,"-N",String(r),"BSpace")),G({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Us(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 ut(N,B("send-keys","-t",t,"-l",s)),n>0&&await mo(n);G({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function vt(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 ut(N,B("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await mo(s),await ut(N,B("send-keys","-t",t,"Enter"))}G({flow:"tmux",phase:"send",sessionName:t,kind:o||"text",textLen:typeof e=="string"?e.length:0,textHead:typeof e=="string"?e.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}import s0 from"node-pty";async function of(t,e,n={}){if(!yt(e)){Hs(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await Je()){Hs(t,`host tmux server not running on ${Ft}`),t.close(1011,"tmux server unavailable");return}if(!await Y(e)){Hs(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=rf(n.cols,120,sf),o=rf(n.rows,32,af),s;try{s=s0.spawn(N,["-S",Ft,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Hs(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Qi(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Qi(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"&&a0(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{}In(e).catch(()=>{})})}var sf=20,af=6;function a0(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=sf&&Number(e)>=af}function rf(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Qi(t,e){try{t.send(JSON.stringify(e))}catch{}}function Hs(t,e){Qi(t,{type:"error",message:e})}function cf(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 wo from"node:fs";import i0 from"node:fs/promises";import ir from"node:path";import c0 from"node:os";var lf={"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},l0=2e5,tc=process.env.BOSTRAT_CLAUDE_PROJECTS||ir.join(c0.homedir(),".claude","projects");function ec(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function nc(t){let e=ir.join(tc,ec(t)),n;try{n=wo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=ir.join(e,o);try{let{mtimeMs:a}=wo.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Ee(t,e){if(!e)return null;let n=ir.join(tc,ec(t),`${e}.jsonl`);return wo.existsSync(n)?n:null}function uf(t){if(!t)return[];let e=ir.join(tc,ec(t)),n;try{n=wo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=wo.statSync(ir.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Mt(t,e=131072){let n=await i0.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 u0(t){let e=t.split(`
49
- `);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 go(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=Ee(t,e),o=!0,!r)return null}else if(n){if(r=nc(t),o=!1,!r)return null}else return null;let s;try{s=u0(await Mt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(lf,s.model),i=lf[s.model]||l0;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 d0(t,e){return!t||!e?null:Ee(t,e)}function f0(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(`
50
- `).trim():""}function p0(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 m0(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function rc(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 df(t,e){if(!t.uuid)return null;let n=rc(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 h0=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function oc(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Mt(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
51
- `)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let h=c.attachment;h?.type==="queued_command"&&h.commandMode==="task-notification"&&typeof h.prompt=="string"&&h.prompt.trim()&&o.push({role:"user",text:h.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 h of l.content){let w=h?.type==="tool_use"?h.input?.file_path||h.input?.path:null;typeof w=="string"&&h0.test(w)&&s.set(h.id,w.split("/").pop())}if(m0(l.content)){let h=df(c,s);h&&o.push({role:"assistant",text:"",images:h,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=f0(l.content),d=u==="assistant"?p0(l.content):null,m=u==="user"?df(c,s):null;!f&&!d&&!m||o.push({role:u,text:f,tools:d||void 0,images:m||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var w0=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function ff(t,e,{turns:n=16}={}){let r=d0(t,e);if(!r)return"";let o=await oc(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}:** ${w0(s.text||c,700)} ${i}`.trim()}).join(`
52
-
53
- `):""}import fS from"node:fs";import dr from"node:fs/promises";import fr from"node:path";import g0 from"node:fs/promises";import{execFile as y0}from"node:child_process";import{promisify as b0}from"node:util";var sc=b0(y0),k0=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,pf=new Set,mf=Promise.resolve(),ac=null;function wf(t){ac=typeof t=="function"?t:null}function x0(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function gf({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||pf.has(r)||(pf.add(r),mf=mf.then(()=>S0({sessionName:t,from:e,to:r})).catch(()=>{}))}async function S0({sessionName:t,from:e,to:n}){await G({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await sc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await g0.realpath(r),s=x0(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await sc("npm",["install","-g","@openai/codex"],{env:a,timeout:k0});let i=(await sc(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 hf({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 hf({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function hf(t){if(ac)try{await ac(t)}catch{}}var v0=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,yf=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function T0(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
54
- `);if(!yf.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(v0);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"&&yf.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 Ws=/(^|\n)\s*›/,xf=/^\s*Welcome to Codex\b/;function cr(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
55
- `);for(let r=n.length-1;r>=0;r--)if(xf.test(n[r]))return n.slice(r).join(`
56
- `);return e}function A0(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 E0(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 bf=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function _0(t,e=""){let n=cr(t);if(Ws.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
57
- `),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(bf);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let m=r[d].trim();if(m){if(bf.test(r[d])||/^Press enter/.test(m))break;f=m;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&&!xf.test(r[l])&&A0(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=E0(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var kf=/Enter this one-time code/,I0=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function R0(t,e=""){let n=cr(t);if(Ws.test(n)||!kf.test(n))return null;let r=n.split(`
58
- `),o=r.findIndex(i=>kf.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(I0);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 $0(t,e=""){let n=cr(t);return Ws.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 O0(t,e=""){let n=cr(t);return Ws.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 ic=/Run Everything|Add a follow-up|Plan, search, build anything/;function M0(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||ic.test(n))return null;let r=n.replace(/\r/g,"").split(`
59
- `).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 C0=/click this link to log in|Signing in with the browser/;function N0(t,e=""){let n=String(t);if(!C0.test(n)||ic.test(n))return null;let r=n.replace(/\r/g,"").split(`
60
- `),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 P0(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||ic.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 Sf(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=R0(e,n)||$0(e,n)||O0(e,n)||_0(e,n);if(r)return r}if(t==="cursor"){let r=N0(e,n)||P0(e,n)||M0(e,n);if(r)return r}return T0(e,{provider:t,box:n})}function vf(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 B0=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,cc=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,yo=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Et={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/}},D0=/Trust this workspace|\[a\]\s*Trust/,j0=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function L0(t){let e=String(t||"");if(!Et.codex.block.test(e))return null;let n=cr(e);if(!Et.codex.block.test(n))return null;let r=n.match(j0);return{from:r?.[1]||null,to:r?.[2]||null}}function Ye(t){return new Promise(e=>setTimeout(e,t))}var F0=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,qs=new Map;function Tf(t,e){let n=Date.now(),r=F0();for(let[s,a]of qs)n-a>=r&&qs.delete(s);let o=`${t}::${e}`;return qs.has(o)?!1:(qs.set(o,n),!0)}var U0=68,H0=/^\s*[─━]{10,}\s*$/;function W0(t){let e=0;for(let n of String(t).split(`
61
- `))H0.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function lc(t,e){let n=Et[t];if(!n?.busy)return"idle";if(uc(t,e))return"unknown";if(n.up&&!n.up.test(e))return"idle";if(n.busy.test(e)||n.busyAlt&&n.busyAlt.test(e))return"busy";let r=W0(e);return t==="claude"&&r>0&&r<U0?"unknown":"idle"}var q0=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,K0=15;function Ef(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
62
- `).slice(-K0).join(`
63
- `);return q0.test(n)}function Ze(t,e){return lc(t,e)==="busy"}function Ks(t,e){return lc(t,e)!=="idle"}function Gs(t,e){let n=Et[t];return n?.up?n.up.test(e):!0}function G0(t,e){let n=Et[t];return n?uc(t,e)?!1:n.up.test(e)&&lc(t,e)==="idle":!0}function uc(t,e){let n=Et[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 Af(t,e,n){let r=Date.now()+n,o=0;for(await In(t).catch(()=>{});;){let s=await gt(t);if(G0(e,s)){await Ye(yo);return}if(e==="cursor"&&D0.test(s)&&!Et.cursor.up.test(s)&&o<3){if(!Tf(t,"cursor-trust")){await Ye(cc);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await vt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await Ye(yo);continue}if(e==="codex"&&o<3){let a=L0(s);if(a){if(!Tf(t,`codex-update:${a.from}->${a.to}`)){await Ye(cc);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await vt(t,"3",{enter:!1,kind:"codex-update-skip"}),gf({sessionName:t,from:a.from,to:a.to}),await Ye(yo);continue}}if(Date.now()>r){if(uc(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Et.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"&&Et.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"&&Et.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"&&Et.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)}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:!!Et[e]?.block?.test(s),paneUp:!!Et[e]?.up?.test(s),paneBusy:!!Et[e]?.busy?.test(s)}).catch(()=>{}),await Ye(yo);return}await Ye(cc)}}function _f({agent:t,agentCmd:e}={}){let n=e?ar(e):ar(t||"");return{label:n,realClaude:e?sr(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 zs({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=_f({agent:n,agentCmd:r});return!e||!o&&!s?null:Qe({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function Qe({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=B0()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=_f({agent:r,agentCmd:o});if(i&&t&&await Ge(t),c&&t&&await tr(t),l&&t&&await er(t),(i||c||l)&&n){await Af(n,a,s);return}let u=Et[r]?r:Et[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Af(n,u,s);return}await Ye(yo)}var z0=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),V0=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function _e(t){return t==="codex"?{enter:!0,submitDelayMs:z0}:t==="claude"?{enter:!0,submitDelayMs:V0}:{enter:!0}}import Js from"node:fs/promises";import Cf from"node:path";var J0=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],X0={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 lr(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of J0){let o=0;for(let s of X0[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var Y0=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),Z0=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function If(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))Y0.has(r)&&(e+=o||0),Z0.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function bo(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 Rf=1.5,$f=.75,Q0=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 Mf(t){let e=bo(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||Q0.has(r)||n.push(r);return n}function tS(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(Of(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(Of(n,"assistant",o))}}return e}function Of(t,e,n){let r=lr(n);return{id:t.id,seq:t.seq,ts:t.payload?.ts??t.ts,role:e,boxId:t.boxId??null,provider:t.provider??null,text:n,tags:r?[r.category]:[]}}function ko(t){let e=tS(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=Mf(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 Vs(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(Mf(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,m=Math.log(1+(t.N-d+.5)/(d+.5));for(let[h,w]of f){let g=t.lengths[h],b=w+Rf*(1-$f+$f*(g/(t.avgdl||1))),S=m*(w*(Rf+1)/b);c.set(h,(c.get(h)||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 Nf="Relevant prior discussion (verbatim, retrieved";async function eS(t){let e=Cf.join(t,".bostrat","events"),n;try{n=await Js.readdir(e)}catch{return ko([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Js.readFile(Cf.join(e,o),"utf8");for(let a of s.split(`
64
- `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return ko(r)}function nS(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}
65
- ${o}`.trim()||e.slice(0,2e3)}function rS(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Vs(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?["",`## ${Nf}, 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(`
66
- `):""}async function Pf(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Js.readFile(e,"utf8");if(a.includes(Nf))return{appended:!1,reason:"already-enriched"};let i=await eS(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=rS(i,nS(a),{k:r,maxChars:o,exclude:s});return c?(await Js.appendFile(e,`
45
+ `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${ar}: ${e?.message||e}`)}}async function V(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&&Ds())try{e.paneTail=mo((await ht(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(Ds()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed)/.test(String(e.phase||e.step||"")))&&await p0(e)}async function wf({limit:t=80}={}){try{return(await po.readFile(ar,"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 gf={DEBUG_DIR:Xi,LOG_FILE:ar};var lt=h0(m0);function ho(t){return new Promise(e=>setTimeout(e,t))}function js(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 N=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux",Ut=process.env.BOSTRAT_TMUX_SOCKET||"/tmp/tmux-1000/default",g0=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Zi=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Qi="bostrat-usage-probe",y0=new Set([Qi]);function tc(t,e){let n=ir(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function ir(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function cr(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}async function ec(t,{loginShell:e=!1,failOpen:n=!1}={}){if(!t)return!1;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await lt(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function Ls(t){return ec(String(t||"").trim().split(/\s+/)[0])}var nc={claude:"npm install -g @anthropic-ai/claude-code",codex:"npm install -g @openai/codex","cursor-agent":"curl https://cursor.com/install -fsS | bash",gh:"apt install gh (Debian: enable cli.github.com/packages) / brew install gh \u2014 see https://github.com/cli/cli#installation"};function P(...t){return["-S",Ut,...t]}async function Je(){if(!Yi.existsSync(Ut))return!1;try{return await lt(N,P("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function wo(){if(await Je())return!0;try{Yi.mkdirSync(Ut.replace(/\/[^/]+$/,""),{recursive:!0}),await lt(N,P("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Je()}var b0=/^[A-Za-z0-9_.-]{1,64}$/;function wt(t){return typeof t=="string"&&b0.test(t)}async function _n(){if(!Yi.existsSync(Ut))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 lt(N,P("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
47
+ `).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d]=r.split("|"),p=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(p)?n-p*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null)}}).filter(r=>!y0.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function Z(t){return(await _n()).find(n=>n.name===t)||null}var k0=/^[A-Za-z0-9 ._↻-]+$/,x0=64;function S0(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=x0&&k0.test(e)}function rc(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function v0(t){try{let{stdout:e}=await lt(N,P("list-sessions","-F","#{session_name}"));return e.split(`
48
+ `).map(n=>n.trim()).includes(t)}catch{return!1}}async function In(t,e=g0,n={}){if(!S0(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=rc(r);if(!wt(o))throw new Error("resulting tmux session name is invalid");if(!await wo())throw new Error(`host tmux server not running on ${Ut} and auto-start failed \u2014 start one: ${N} -S ${Ut} new-session -d -s keeper "sleep 2147483647"`);if(await v0(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Zi,a=n.agent||cr(s),i=!!n.resumeSessionId,c=n.resumeSessionId||w0(),l=i?s:tc(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&ir(s),d=n.firstRun===!0;d||(f&&await Ke(e),a==="codex"&&await er(e),a==="cursor"&&await nr(e),await Zd(e),await pf(e));let p=f&&!d?_s():null,m=_d(),g=["-e",`${Ts}=${m}`];p&&g.push("-e",`ANTHROPIC_API_KEY=${p}`);try{await lt(N,P("new-session","-d","-s",o,"-c",e,...g))}catch(y){throw js(p?js(y,p):y,m)}return await Rn(o),await lt(N,P("set-option","-t",o,"@bostrat-session","1")),await lt(N,P("set-option","-t",o,"@bostrat-title",r)),await lt(N,P("set-option","-t",o,"@bostrat-sid",c)),await lt(N,P("set-option","-t",o,"@bostrat-agent",a)),await lt(N,P("send-keys","-t",o,l,"Enter")),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function ce(t){if(!wt(t))throw new Error("invalid session name");await lt(N,P("kill-session","-t",t))}async function yf(t,e){if(wt(t))try{let n=e?P("set-option","-t",t,"@bostrat-sid",e):P("set-option","-u","-t",t,"@bostrat-sid");await lt(N,n)}catch{}}async function ht(t,{lines:e=40}={}){if(!wt(t))return"";try{let n=P("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await lt(N,n);return String(r||"")}catch{return""}}var T0=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,A0=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Tt=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,E0=Number(process.env.BOSTRAT_BOX_COLS)||120,_0=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function bf(t){if(!wt(t))return 0;try{let{stdout:e}=await lt(N,P("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
49
+ `).filter(Boolean).length}catch{return 0}}async function Rn(t,{cols:e=E0,rows:n=_0}={}){if(wt(t)&&!await bf(t))try{let r=await xf(t);if(r.cols===e&&r.rows===n)return;await lt(N,P("resize-window","-t",t,"-x",String(e),"-y",String(n))),await lt(N,P("set-window-option","-t",t,"window-size","latest")),await ho(80)}catch{}}async function kf(t){if(!wt(t))return 0;try{let{stdout:e}=await lt(N,P("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function xf(t){if(!wt(t))return{cols:0,rows:0};try{let{stdout:e}=await lt(N,P("display-message","-t",t,"-p","#{window_width} #{window_height}")),[n,r]=String(e||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function Fs(t,{cols:e=T0,rows:n=A0}={}){if(wt(t)&&!await bf(t))try{let r=await xf(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await lt(N,P("resize-window","-t",t,"-x",String(o),"-y",String(s))),await lt(N,P("set-window-option","-t",t,"window-size","latest")),await ho(80))}catch{}}async function Xe(t){return await Fs(t),ht(t,{lines:Tt})}var I0=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function bt(t,e,{settleMs:n=40,kind:r}={}){if(!wt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>I0.has(s));for(let s=0;s<o.length;s++)await lt(N,P("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await ho(n);return V({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Us(t,e,{kind:n}={}){if(!wt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await lt(N,P("send-keys","-t",t,"-N",String(r),"BSpace")),V({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Hs(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!wt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await lt(N,P("send-keys","-t",t,"-l",s)),n>0&&await ho(n);V({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function xt(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!wt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await lt(N,P("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await ho(s),await lt(N,P("send-keys","-t",t,"Enter"))}V({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 R0 from"node-pty";async function vf(t,e,n={}){if(!wt(e)){Ws(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await Je()){Ws(t,`host tmux server not running on ${Ut}`),t.close(1011,"tmux server unavailable");return}if(!await Z(e)){Ws(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=Sf(n.cols,120,Tf),o=Sf(n.rows,32,Af),s;try{s=R0.spawn(N,["-S",Ut,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Ws(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&oc(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(oc(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"&&$0(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{}Rn(e).catch(()=>{})})}var Tf=20,Af=6;function $0(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=Tf&&Number(e)>=Af}function Sf(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function oc(t,e){try{t.send(JSON.stringify(e))}catch{}}function Ws(t,e){oc(t,{type:"error",message:e})}function Ef(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 go from"node:fs";import O0 from"node:fs/promises";import lr from"node:path";import C0 from"node:os";var _f={"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},M0=2e5,sc=process.env.BOSTRAT_CLAUDE_PROJECTS||lr.join(C0.homedir(),".claude","projects");function ac(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function ic(t){let e=lr.join(sc,ac(t)),n;try{n=go.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=lr.join(e,o);try{let{mtimeMs:a}=go.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Ae(t,e){if(!e)return null;let n=lr.join(sc,ac(t),`${e}.jsonl`);return go.existsSync(n)?n:null}function If(t){if(!t)return[];let e=lr.join(sc,ac(t)),n;try{n=go.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=go.statSync(lr.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Nt(t,e=131072){let n=await O0.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 N0(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 yo(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=Ae(t,e),o=!0,!r)return null}else if(n){if(r=ic(t),o=!1,!r)return null}else return null;let s;try{s=N0(await Nt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(_f,s.model),i=_f[s.model]||M0;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 P0(t,e){return!t||!e?null:Ae(t,e)}function B0(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 D0(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 j0(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function cc(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 Rf(t,e){if(!t.uuid)return null;let n=cc(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 L0=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function lc(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Nt(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 g=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof g=="string"&&L0.test(g)&&s.set(m.id,g.split("/").pop())}if(j0(l.content)){let m=Rf(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=B0(l.content),d=u==="assistant"?D0(l.content):null,p=u==="user"?Rf(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 F0=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function $f(t,e,{turns:n=16}={}){let r=P0(t,e);if(!r)return"";let o=await lc(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}:** ${F0(s.text||c,700)} ${i}`.trim()}).join(`
53
+
54
+ `):""}import BS from"node:fs";import pr from"node:fs/promises";import mr from"node:path";import U0 from"node:fs/promises";import{execFile as H0}from"node:child_process";import{promisify as W0}from"node:util";var uc=W0(H0),q0=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Of=new Set,Cf=Promise.resolve(),dc=null;function Nf(t){dc=typeof t=="function"?t:null}function K0(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function Pf({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||Of.has(r)||(Of.add(r),Cf=Cf.then(()=>G0({sessionName:t,from:e,to:r})).catch(()=>{}))}async function G0({sessionName:t,from:e,to:n}){await V({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await uc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await U0.realpath(r),s=K0(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await uc("npm",["install","-g","@openai/codex"],{env:a,timeout:q0});let i=(await uc(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await V({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await Mf({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await V({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await Mf({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function Mf(t){if(dc)try{await dc(t)}catch{}}var V0=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Bf=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function z0(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
55
+ `);if(!Bf.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(V0);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"&&Bf.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 qs=/(^|\n)\s*›/,Lf=/^\s*Welcome to Codex\b/;function ur(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
56
+ `);for(let r=n.length-1;r>=0;r--)if(Lf.test(n[r]))return n.slice(r).join(`
57
+ `);return e}function J0(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 X0(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 Df=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function Y0(t,e=""){let n=ur(t);if(qs.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(Df);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(Df.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&&!Lf.test(r[l])&&J0(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=X0(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var jf=/Enter this one-time code/,Z0=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function Q0(t,e=""){let n=ur(t);if(qs.test(n)||!jf.test(n))return null;let r=n.split(`
59
+ `),o=r.findIndex(i=>jf.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(Z0);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=ur(t);return qs.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 eS(t,e=""){let n=ur(t);return qs.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 nS(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 rS=/click this link to log in|Signing in with the browser/;function oS(t,e=""){let n=String(t);if(!rS.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 sS(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 Ff(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=Q0(e,n)||tS(e,n)||eS(e,n)||Y0(e,n);if(r)return r}if(t==="cursor"){let r=oS(e,n)||sS(e,n)||nS(e,n);if(r)return r}return z0(e,{provider:t,box:n})}function Uf(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 aS=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,pc=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,bo=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,At={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/}},iS=/Trust this workspace|\[a\]\s*Trust/,cS=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function lS(t){let e=String(t||"");if(!At.codex.block.test(e))return null;let n=ur(e);if(!At.codex.block.test(n))return null;let r=n.match(cS);return{from:r?.[1]||null,to:r?.[2]||null}}function Ye(t){return new Promise(e=>setTimeout(e,t))}var uS=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Ks=new Map;function Hf(t,e){let n=Date.now(),r=uS();for(let[s,a]of Ks)n-a>=r&&Ks.delete(s);let o=`${t}::${e}`;return Ks.has(o)?!1:(Ks.set(o,n),!0)}var dS=68,fS=/^\s*[─━]{10,}\s*$/;function pS(t){let e=0;for(let n of String(t).split(`
62
+ `))fS.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function mc(t,e){let n=At[t];if(!n?.busy)return"idle";if(hc(t,e))return"unknown";if(n.up&&!n.up.test(e))return"idle";if(n.busy.test(e)||n.busyAlt&&n.busyAlt.test(e))return"busy";let r=pS(e);return t==="claude"&&r>0&&r<dS?"unknown":"idle"}var mS=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,hS=15;function qf(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
+ `).slice(-hS).join(`
64
+ `);return mS.test(n)}function Ze(t,e){return mc(t,e)==="busy"}function Gs(t,e){return mc(t,e)!=="idle"}function Vs(t,e){let n=At[t];return n?.up?n.up.test(e):!0}function wS(t,e){let n=At[t];return n?hc(t,e)?!1:n.up.test(e)&&mc(t,e)==="idle":!0}function hc(t,e){let n=At[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 Wf(t,e,n){let r=Date.now()+n,o=0;for(await Rn(t).catch(()=>{});;){let s=await ht(t);if(wS(e,s)){await Ye(bo);return}if(e==="cursor"&&iS.test(s)&&!At.cursor.up.test(s)&&o<3){if(!Hf(t,"cursor-trust")){await Ye(pc);continue}o+=1,await V({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await xt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await Ye(bo);continue}if(e==="codex"&&o<3){let a=lS(s);if(a){if(!Hf(t,`codex-update:${a.from}->${a.to}`)){await Ye(pc);continue}o+=1,await V({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await xt(t,"3",{enter:!1,kind:"codex-update-skip"}),Pf({sessionName:t,from:a.from,to:a.to}),await Ye(bo);continue}}if(Date.now()>r){if(hc(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=At.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"&&At.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"&&At.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"&&At.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'),V({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),V({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!At[e]?.block?.test(s),paneUp:!!At[e]?.up?.test(s),paneBusy:!!At[e]?.busy?.test(s)}).catch(()=>{}),await Ye(bo);return}await Ye(pc)}}function Kf({agent:t,agentCmd:e}={}){let n=e?cr(e):cr(t||"");return{label:n,realClaude:e?ir(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 zs({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Kf({agent:n,agentCmd:r});return!e||!o&&!s?null:Qe({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function Qe({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=aS()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Kf({agent:r,agentCmd:o});if(i&&t&&await Ke(t),c&&t&&await er(t),l&&t&&await nr(t),(i||c||l)&&n){await Wf(n,a,s);return}let u=At[r]?r:At[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Wf(n,u,s);return}await Ye(bo)}var gS=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),yS=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function Ee(t){return t==="codex"?{enter:!0,submitDelayMs:gS}:t==="claude"?{enter:!0,submitDelayMs:yS}:{enter:!0}}import Xs from"node:fs/promises";import Yf from"node:path";var bS=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],kS={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 dr(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of bS){let o=0;for(let s of kS[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var xS=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),SS=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Gf(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))xS.has(r)&&(e+=o||0),SS.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function ko(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 Vf=1.5,zf=.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 Xf(t){let e=ko(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 TS(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(Jf(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(Jf(n,"assistant",o))}}return e}function Jf(t,e,n){let r=dr(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 xo(t){let e=TS(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=Xf(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 Js(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(Xf(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,g]of f){let y=t.lengths[m],b=g+Vf*(1-zf+zf*(y/(t.avgdl||1))),S=p*(g*(Vf+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 Zf="Relevant prior discussion (verbatim, retrieved";async function AS(t){let e=Yf.join(t,".bostrat","events"),n;try{n=await Xs.readdir(e)}catch{return xo([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Xs.readFile(Yf.join(e,o),"utf8");for(let a of s.split(`
65
+ `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return xo(r)}function ES(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 _S(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Js(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?["",`## ${Zf}, 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 Qf(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Xs.readFile(e,"utf8");if(a.includes(Zf))return{appended:!1,reason:"already-enriched"};let i=await AS(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=_S(i,ES(a),{k:r,maxChars:o,exclude:s});return c?(await Xs.appendFile(e,`
67
68
  `+c),{appended:!0,chars:c.length,chunks:c.split(`
68
- - `).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 dc from"node:fs/promises";import ur from"node:path";import oS from"node:os";import{EventEmitter as sS}from"node:events";import{randomUUID as aS}from"node:crypto";var iS=new Set(["session.ready","turn.user","turn.agent","turn.retracted","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","provider.connected","track.summary.recorded","changelog.published","stall.detected","error"]),cS=new Set(["user","agent","orchestrator","system"]),lS=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),zt=new sS;zt.setMaxListeners(0);var fc=process.env.BOSTRAT_STATE_DIR||ur.join(process.env.HOME||oS.tmpdir(),".bostrat");function Ys(t){return t?.cwd?ur.join(t.cwd,".bostrat","events"):ur.join(fc,"events")}var Bf=(t,e)=>ur.join(t,`${e}.ndjson`),Xs=new Map;async function uS(t,e){if(!Xs.has(e)){let o=(await L(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);Xs.set(e,o)}let n=Xs.get(e)+1;return Xs.set(e,n),n}function dS(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function $(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!iS.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!cS.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!lS.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?ur.join(fc,"events"):Ys(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=dS(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await uS(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||aS(),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 dc.mkdir(r,{recursive:!0}),await dc.appendFile(Bf(r,n),JSON.stringify(i)+`
69
- `),zt.emit("event",i),zt.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"?ur.join(fc,"events"):Ys(t));let o;try{o=await dc.readFile(Bf(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
70
- `))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function Df(t,e){let n=`track:${t}`;return zt.on(n,e),()=>zt.off(n,e)}var Uf=".bostrat/handoff",Hf="latest.md",jf=32*1024;function pS(t){let e=po(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=jf?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,jf).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 wc from"node:fs/promises";import fr from"node:path";import IS from"node:os";import{EventEmitter as RS}from"node:events";import{randomUUID as $S}from"node:crypto";var OS=new Set(["session.ready","turn.user","turn.agent","turn.retracted","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","provider.connected","track.summary.recorded","changelog.published","stall.detected","error"]),CS=new Set(["user","agent","orchestrator","system"]),MS=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Jt=new RS;Jt.setMaxListeners(0);var gc=process.env.BOSTRAT_STATE_DIR||fr.join(process.env.HOME||IS.tmpdir(),".bostrat");function Zs(t){return t?.cwd?fr.join(t.cwd,".bostrat","events"):fr.join(gc,"events")}var tp=(t,e)=>fr.join(t,`${e}.ndjson`),Ys=new Map;async function NS(t,e){if(!Ys.has(e)){let o=(await D(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);Ys.set(e,o)}let n=Ys.get(e)+1;return Ys.set(e,n),n}function PS(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||!OS.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!CS.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!MS.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?fr.join(gc,"events"):Zs(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=PS(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await NS(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||$S(),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 wc.mkdir(r,{recursive:!0}),await wc.appendFile(tp(r,n),JSON.stringify(i)+`
70
+ `),Jt.emit("event",i),Jt.emit(`track:${n}`,i),i}async function D(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?fr.join(gc,"events"):Zs(t));let o;try{o=await wc.readFile(tp(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 ep(t,e){let n=`track:${t}`;return Jt.on(n,e),()=>Jt.off(n,e)}var sp=".bostrat/handoff",ap="latest.md",np=32*1024;function DS(t){let e=mo(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=np?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,np).toString("utf8").replace(/�+$/,"")}
71
72
 
72
- [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var Wf="Checkpoint: write a work-in-progress handoff",qf="HANDOFF WRITTEN",Kf="Resume from .bostrat/handoff/latest.md",mS="/clear",hS=t=>`${Wf} 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 ${qf} and take no further action.`,wS=`${Kf}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Gf="Beat: write a",zf="BEAT WRITTEN",gS=".bostrat/arc",yS="beat.txt",Lf=200,Vf=4,Jf=5,bS=36,kS=t=>`${Gf} 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 ${Vf} further lines, each starting with "- " and naming ONE piece in at most ${Jf} 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 ${zf} and take no further action.`,Ff=/^(?:[-–—*•·]|\d+[.)])\s+/,xS=(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 SS(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Ff.test(s)){if(r.length>=Vf)continue;let a=xS(s.replace(Ff,"").replace(/[.;,:]+$/,""),Jf,bS);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>Lf&&(o=`${o.slice(0,Lf-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Xf(t,e,{timeoutMs:n=9e4}={}){let r=fr.join(e,gS),o=fr.join(r,yS);await dr.mkdir(r,{recursive:!0});let s=await Zs(o),a=await Y(t);if(!a)throw new Error(`session "${t}" not found`);return await Qs(t,a,kS(o)),await op(o,s,n),SS(await dr.readFile(o,"utf8"))}function Yf(t,e){let n=(e||"").trim();return n?t==="assistant"?n===qf||n===zf:n.startsWith(Wf)||n===mS||n.startsWith(Kf)||n.startsWith(Gf):!1}var vS=6e5;function Zf(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-vS;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 Qf(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 tp(t,{windows:e=[]}={}){if(Yf(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 TS=3500,AS=3e5,pc=new Map;function ta(t){return pc.get(t)||null}async function ep(t){let e=pc.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await Y(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:fr.join(n.cwd,Uf,Hf)};return pc.set(t,r),ES(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function ES(t,e){t.step="summarize",await np(t.name,e),t.step="clear";let n=await Y(t.name);await Qs(t.name,n,"/clear"),await sp(TS),t.step="resume",await Qs(t.name,n,wS),t.step="done",t.finishedAt=Date.now()}async function np(t,e){let n=fr.join(e,Uf),r=fr.join(n,Hf);await dr.mkdir(n,{recursive:!0});let o=await Zs(r),s=await Y(t);await G({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await Qs(t,s,hS(r)),await op(r,o,AS),await G({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await Zs(r)});let a=await Pf(e,r,{exclude:c=>Yf(c.role,c.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 $({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 c=await dr.readFile(r,"utf8");await $({id:i,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...pS(c),ts:Date.now()}})}catch{}}return await Vd(e),await _S(n,r),r}async function rp(t){let e=await Y(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return np(t,e.cwd)}async function Zs(t){try{return(await dr.stat(t)).mtimeMs}catch{return 0}}async function op(t,e,n){let r=Date.now()+n;for(;;){if(await Zs(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await sp(2e3)}}async function _S(t,e){try{if(!fS.existsSync(e))return;let n=new Date().toISOString().replace(/[:.]/g,"-");await dr.copyFile(e,fr.join(t,`${n}.md`))}catch{}}function sp(t){return new Promise(e=>setTimeout(e,t))}async function Qs(t,e,n){if(e){let o=e.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await Qe({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await Y(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await vt(t,n,{kind:"orchestration-drive",..._e(r.agent)})}import U from"node:fs/promises";import H from"node:path";import{randomUUID as cs}from"node:crypto";import{execFile as z_}from"node:child_process";import{promisify as V_}from"node:util";import Ym from"node:path";var mc={"claude-opus-4-8":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"claude-sonnet-5":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-sonnet-4-6":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-haiku-4-5":{input:1,output:5,cacheWrite:1.25,cacheRead:.1},"claude-fable-5":{input:10,output:50,cacheWrite:12.5,cacheRead:1},"gpt-5.5":{input:5,output:30,cacheWrite:5,cacheRead:.5},"gpt-5.6-sol":{input:5,output:30,cacheWrite:6.25,cacheRead:.5},"gpt-5.6-terra":{input:2.5,output:15,cacheWrite:3.125,cacheRead:.25},"gpt-5.6-luna":{input:1,output:6,cacheWrite:1.25,cacheRead:.1}},ap={opus:"claude-opus-4-8",opusplan:"claude-opus-4-8",default:"claude-opus-4-8",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"};function ip(t){if(!t)return null;let e=String(t).trim();return mc[e]?e:ap[e]?ap[e]:Object.keys(mc).find(r=>e.startsWith(r))||null}var ea=t=>ip(t)!==null;function le(t,e={}){let n=mc[ip(t)];return n?((e.input||0)*n.input+(e.output||0)*n.output+(e.cacheWrite||0)*n.cacheWrite+(e.cacheRead||0)*n.cacheRead)/1e6:0}var cp=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function IS(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function lp(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function ra(t){let e=t.payload?.model||"unknown",n=IS(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=!1;!r&&n&&(r=le(e,n),r>0&&(o=!0));let s=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,a=!r&&s>0&&!ea(e);return{usd:r,model:e,tokens:n,repriced:o,unpriced:a}}function ue(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=cp();for(let f of n){let{usd:d,model:m,tokens:h,repriced:w,unpriced:g}=ra(f),b=f.provider||"unknown",S=f.boxId||"unattributed";if(w&&i++,g&&(c++,l.add(m)),a+=d,lp(u,h),r[m]||={usd:0,tokens:cp(),events:0},r[m].usd+=d,r[m].events+=1,lp(r[m].tokens,h),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,m!=="unknown"&&s[S].models.add(m),f.epoch!=null){let A=(s[S].epochs||={})[f.epoch]||={usd:0,events:0,provider:b,models:new Set};A.usd+=d,A.events+=1,A.provider=b,m!=="unknown"&&A.models.add(m)}}return{totalUsd:xo(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:na(r,f=>({...f,usd:xo(f.usd)})),byProvider:na(o,f=>({...f,usd:xo(f.usd)})),byBox:na(s,f=>({usd:xo(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:na(f.epochs,d=>({usd:xo(d.usd),events:d.events,provider:d.provider,models:[...d.models]}))}:{}}))}}var xo=t=>Math.round(t*1e4)/1e4,na=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import St from"node:fs/promises";import Nt from"node:path";import Ao from"node:os";import{randomUUID as LS}from"node:crypto";import{execFile as FS}from"node:child_process";import{promisify as US}from"node:util";import{chromium as HS}from"playwright-core";import hc from"node:fs/promises";import RS from"node:path";var $S=0;async function Ct(t,e,{mode:n}={}){await hc.mkdir(RS.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++$S}.tmp`;await hc.writeFile(r,JSON.stringify(e,null,2)+`
73
- `,n!=null?{mode:n}:void 0),await hc.rename(r,t)}import fp from"node:fs/promises";import pp from"node:os";import mp from"node:path";var OS=1e4,MS=3e4,up=2e3,CS=()=>process.env.BOSTRAT_STATE_DIR||mp.join(process.env.HOME||pp.tmpdir(),".bostrat"),hp=()=>mp.join(CS(),"content-outbox.json"),NS=()=>process.env.BOSTRAT_NODE_ID||pp.hostname(),PS=(...t)=>fetch(...t),BS=MS;function Tt(t=process.env){return!!(Gt(t)&&t.BOSTRAT_PLANE_TOKEN)}var vo=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function To(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),OS);try{return await PS(`${Gt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var sa=null,wc=null,oa=null,So=null;async function wp(){return sa||(wc||=fp.readFile(hp(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>sa={ops:Array.isArray(t?.ops)?t.ops:[]}).finally(()=>wc=null),wc)}async function gp(){await Ct(hp(),sa).catch(()=>{})}function dp(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function gc(t){let e=await wp(),n=dp(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=>dp(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>up&&(e.ops=e.ops.slice(-up)),await gp()}function DS(){oa||(oa=setTimeout(()=>{oa=null,pr().catch(()=>{})},BS),oa.unref?.())}async function jS(t,e=process.env){if(t.op==="record"){let n=await To(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...vo(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:NS()}]})},e);if(!n.ok)throw new Error(`plane ${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 fp.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await To(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...vo(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw new Error(`plane ${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 To(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:vo(e)},e);if(!n.ok)throw new Error(`plane ${n.status}`);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function pr({env:t=process.env,log:e=console}={}){return Tt(t)?So||(So=(async()=>{let n=await wp(),r=0;for(;n.ops.length;){let o=n.ops[0],s;try{s=await jS(o,t)}catch(a){return DS(),{flushed:r,pending:n.ops.length,error:a?.message||String(a)}}s.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${o.op} ${o.family}/${o.id}: ${s.rejected}`),n.ops.shift(),r++,await gp()}return{flushed:r,pending:0}})().finally(()=>{So=null}),So):{flushed:0,source:"unconfigured"}}async function de(t,e,{trackId:n=null,env:r=process.env}={}){return Tt(r)?(await gc({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),pr({env:r})):{queued:!1,source:"unconfigured"}}async function mr(t,e,n,r,o,{env:s=process.env}={}){return Tt(s)?(await gc({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),pr({env:s})):{queued:!1,source:"unconfigured"}}async function Yt(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return Tt(r)?(await gc({op:"delete",family:t,id:e,deletedAt:n}),pr({env:r})):{queued:!1,source:"unconfigured"}}async function Zt(t,{env:e=process.env}={}){if(!Tt(e))return{records:[],source:"unconfigured"};try{let n=await To(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:vo(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 yp(t,e,n,r,{env:o=process.env}={}){if(!Tt(o))return null;try{let s=await To(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:vo(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 kp=US(FS),xp=process.env.BOSTRAT_STATE_DIR||Nt.join(process.env.HOME||Ao.tmpdir(),".bostrat"),Sp=Nt.join(xp,"mocks.json"),WS=process.env.BOSTRAT_MOCK_ENGINE||"chromium",qS=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",KS=process.env.BOSTRAT_FIREFOX_BIN||"firefox",GS=1300,hr=2,zS=32,vp=1500,wr=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,VS=700*1024*1024;async function JS(){if(process.platform!=="darwin")return Ao.freemem();try{let{stdout:t}=await kp("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:Ao.freemem()}catch{return Ao.freemem()}}var Qt=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 ip="Checkpoint: write a work-in-progress handoff",cp="HANDOFF WRITTEN",lp="Resume from .bostrat/handoff/latest.md",jS="/clear",LS=t=>`${ip} 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 ${cp} and take no further action.`,FS=`${lp}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,up="Beat: write a",dp="BEAT WRITTEN",US=".bostrat/arc",HS="beat.txt",rp=200,fp=4,pp=5,WS=36,qS=t=>`${up} 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 ${fp} further lines, each starting with "- " and naming ONE piece in at most ${pp} 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 ${dp} and take no further action.`,op=/^(?:[-–—*•·]|\d+[.)])\s+/,KS=(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 GS(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(op.test(s)){if(r.length>=fp)continue;let a=KS(s.replace(op,"").replace(/[.;,:]+$/,""),pp,WS);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>rp&&(o=`${o.slice(0,rp-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function mp(t,e,{timeoutMs:n=9e4}={}){let r=mr.join(e,US),o=mr.join(r,HS);await pr.mkdir(r,{recursive:!0});let s=await Qs(o),a=await Z(t);if(!a)throw new Error(`session "${t}" not found`);return await ta(t,a,qS(o)),await Sp(o,s,n),GS(await pr.readFile(o,"utf8"))}function hp(t,e){let n=(e||"").trim();return n?t==="assistant"?n===cp||n===dp:n.startsWith(ip)||n===jS||n.startsWith(lp)||n.startsWith(up):!1}var VS=6e5;function wp(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-VS;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 gp(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 yp(t,{windows:e=[]}={}){if(hp(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 zS=3500,JS=3e5,yc=new Map;function ea(t){return yc.get(t)||null}async function bp(t){let e=yc.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await Z(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:mr.join(n.cwd,sp,ap)};return yc.set(t,r),XS(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function XS(t,e){t.step="summarize",await kp(t.name,e),t.step="clear";let n=await Z(t.name);await ta(t.name,n,"/clear"),await vp(zS),t.step="resume",await ta(t.name,n,FS),t.step="done",t.finishedAt=Date.now()}async function kp(t,e){let n=mr.join(e,sp),r=mr.join(n,ap);await pr.mkdir(n,{recursive:!0});let o=await Qs(r),s=await Z(t);await V({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await ta(t,s,LS(r)),await Sp(r,o,JS),await V({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await Qs(r)});let a=await Qf(e,r,{exclude:l=>hp(l.role,l.text)});await V({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 pr.readFile(r,"utf8");await R({id:i,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...DS(l),ts:Date.now()}})}catch{}}await ef(e);let c=await YS(n,r);return i&&await ff(i,c||r,{box:t,agent:s?.agent??null}),r}async function xp(t){let e=await Z(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return kp(t,e.cwd)}async function Qs(t){try{return(await pr.stat(t)).mtimeMs}catch{return 0}}async function Sp(t,e,n){let r=Date.now()+n;for(;;){if(await Qs(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await vp(2e3)}}async function YS(t,e){try{if(!BS.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=mr.join(t,`${n}.md`);return await pr.copyFile(e,r),r}catch{return null}}function vp(t){return new Promise(e=>setTimeout(e,t))}async function ta(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 Qe({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await Z(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await xt(t,n,{kind:"orchestration-drive",...Ee(r.agent)})}import L from"node:fs/promises";import F from"node:path";import{randomUUID as as}from"node:crypto";import{execFile as cI}from"node:child_process";import{promisify as lI}from"node:util";import ch from"node:path";var bc={"claude-opus-4-8":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"claude-sonnet-5":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-sonnet-4-6":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-haiku-4-5":{input:1,output:5,cacheWrite:1.25,cacheRead:.1},"claude-fable-5":{input:10,output:50,cacheWrite:12.5,cacheRead:1},"gpt-5.5":{input:5,output:30,cacheWrite:5,cacheRead:.5},"gpt-5.6-sol":{input:5,output:30,cacheWrite:6.25,cacheRead:.5},"gpt-5.6-terra":{input:2.5,output:15,cacheWrite:3.125,cacheRead:.25},"gpt-5.6-luna":{input:1,output:6,cacheWrite:1.25,cacheRead:.1}},Tp={opus:"claude-opus-4-8",opusplan:"claude-opus-4-8",default:"claude-opus-4-8",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"};function Ap(t){if(!t)return null;let e=String(t).trim();return bc[e]?e:Tp[e]?Tp[e]:Object.keys(bc).find(r=>e.startsWith(r))||null}var na=t=>Ap(t)!==null;function le(t,e={}){let n=bc[Ap(t)];return n?((e.input||0)*n.input+(e.output||0)*n.output+(e.cacheWrite||0)*n.cacheWrite+(e.cacheRead||0)*n.cacheRead)/1e6:0}var Ep=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function ZS(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function _p(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function oa(t){let e=t.payload?.model||"unknown",n=ZS(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=!1;!r&&n&&(r=le(e,n),r>0&&(o=!0));let s=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,a=!r&&s>0&&!na(e);return{usd:r,model:e,tokens:n,repriced:o,unpriced:a}}function ue(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=Ep();for(let f of n){let{usd:d,model:p,tokens:m,repriced:g,unpriced:y}=oa(f),b=f.provider||"unknown",S=f.boxId||"unattributed";if(g&&i++,y&&(c++,l.add(p)),a+=d,_p(u,m),r[p]||={usd:0,tokens:Ep(),events:0},r[p].usd+=d,r[p].events+=1,_p(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:So(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:ra(r,f=>({...f,usd:So(f.usd)})),byProvider:ra(o,f=>({...f,usd:So(f.usd)})),byBox:ra(s,f=>({usd:So(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:ra(f.epochs,d=>({usd:So(d.usd),events:d.events,provider:d.provider,models:[...d.models]}))}:{}}))}}var So=t=>Math.round(t*1e4)/1e4,ra=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import kt from"node:fs/promises";import Pt from"node:path";import vo from"node:os";import{randomUUID as QS}from"node:crypto";import{execFile as tv}from"node:child_process";import{promisify as ev}from"node:util";import{chromium as nv}from"playwright-core";var Rp=ev(tv),$p=process.env.BOSTRAT_STATE_DIR||Pt.join(process.env.HOME||vo.tmpdir(),".bostrat"),Op=Pt.join($p,"mocks.json"),rv=process.env.BOSTRAT_MOCK_ENGINE||"chromium",ov=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",sv=process.env.BOSTRAT_FIREFOX_BIN||"firefox",av=1300,hr=2,iv=32,Cp=1500,wr=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,cv=700*1024*1024;async function lv(){if(process.platform!=="darwin")return vo.freemem();try{let{stdout:t}=await Rp("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:vo.freemem()}catch{return vo.freemem()}}var te=class extends Error{constructor(e,n=""){super(n?`${e}
74
74
  ${n.trim().split(`
75
75
  `).slice(-6).join(`
76
- `)}`:e),this.name="MockRenderError"}},Rn=null,yc=null;async function pe(){return Rn||(yc||=(async()=>{try{Rn=JSON.parse(await St.readFile(Sp,"utf8"))}catch{Rn={mocks:[]}}finally{yc=null}return Rn.mocks||=[],Rn})(),yc)}async function fe(){await Ct(Sp,Rn)}var tn=t=>de("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),$n=(t,e,n)=>mr("mocks",t.id,e,n,Ut(t,e)[n]).catch(()=>{});function te(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var aa=t=>Nt.join(t,".bostrat","mocks");function Ut(t,e){let n=aa(t.cwd);return{html:Nt.join(n,`${t.id}.v${e}.html`),png:Nt.join(n,`${t.id}.v${e}.png`)}}function Ie(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 kc(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var XS=Buffer.from([137,80,78,71,13,10,26,10]);function Tp(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(XS)}async function YS(t){let e=Buffer.alloc(24),n=await St.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function ZS({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 QS(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((h,w)=>{r.onload=h,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,m=-1;for(let h=0;h<s;h++)for(let w=h*o*4,g=0;g<o;g++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(g<u&&(u=g),g>d&&(d=g),h<f&&(f=h),m=h);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:m}}},e.toString("base64"))}async function tv(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 ev({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await HS.launch({executablePath:qS,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Qt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:hr})).newPage();a.setDefaultTimeout(wr),await a.goto(`file://${t}`,{waitUntil:"load",timeout:wr}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:wr});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await QS(a,i),u=l&&ZS({...l,pad:zS*hr,align:hr});u&&(i=await tv(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<vp)throw new Qt(`capture produced a ${i.length}-byte image`);let c=kc(i);if(!c)throw new Qt("output is not a PNG");return await St.writeFile(e,i),{width:Math.round(c.width/hr),height:Math.round(c.height/hr),dpr:hr,renderMs:Date.now()-r}}catch(s){throw s instanceof Qt?s:new Qt(/timeout/i.test(s?.message||"")?`render timed out after ${wr}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function bp({htmlPath:t,outPath:e,width:n}){let r=await St.mkdtemp(Nt.join(Ao.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await kp(KS,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:wr,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Qt(c?.killed?`render timed out after ${wr}ms`:"firefox exited non-zero",s)}let a=await St.stat(e).catch(()=>null);if(!a||a.size<vp)throw new Qt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=kc(await YS(e));if(!i)throw new Qt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await St.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function nv(t){if(WS==="firefox")return bp(t);try{return await ev(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return bp(t);throw e}}var rv=nv;var bc=Promise.resolve();function Ap(t){return bc=bc.then(t,t),bc}async function Ep(t,e){let n=await pe(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await fe(),await te(r,"rendering",{version:e});try{let o=await JS();if(o<VS)throw new Qt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Ut(r,e),a=await rv({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await St.rm(s.png,{force:!0}).catch(()=>{});return}_p(r,{v:e,source:"html",htmlRel:Nt.relative(r.cwd,s.html),pngRel:Nt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await fe(),await te(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),tn(r),$n(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await fe(),await te(r,"error",{version:e,error:r.error}),tn(r)}}}function _p(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 ov({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?GS:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function On({trackId:t,group:e}={}){let n=await pe(),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 en(t){let n=(await pe()).mocks.find(r=>r.id===t);return n?{...n}:null}async function Ip({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=ov({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&&!Tp(r))throw new Error("image must be a PNG");let d=await pe(),m={id:LS().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 St.mkdir(aa(o),{recursive:!0}),d.mocks.push(m),f?(await St.writeFile(Ut(m,1).html,n),await fe(),await te(m,"created"),tn(m),$n(m,1,"html"),Ap(()=>Ep(m.id,1))):(await $p(m,1,r),await fe(),await te(m,"created"),await te(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr}),tn(m),$n(m,1,"png")),{...m}}async function Rp(t,{html:e,image:n,width:r}={}){let s=(await pe()).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&&!Tp(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 St.mkdir(aa(s.cwd),{recursive:!0}),a?(s.state="queued",await St.writeFile(Ut(s,i).html,e),await fe(),await te(s,"updated",{version:i}),tn(s),$n(s,i,"html"),Ap(()=>Ep(s.id,i))):(await $p(s,i,n),await fe(),await te(s,"updated",{version:i}),await te(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr}),tn(s),$n(s,i,"png")),{...s}}async function $p(t,e,n){let r=kc(n);if(!r)throw new Error("image must be a PNG");let o=Ut(t,e);await St.writeFile(o.png,n),_p(t,{v:e,source:"image",htmlRel:null,pngRel:Nt.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Op(t){let e=await pe(),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 te(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Mp(t){let e=await pe(),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=Ut(r,o);await St.rm(s.html,{force:!0}).catch(()=>{}),await St.rm(s.png,{force:!0}).catch(()=>{})}return await fe(),await te(r,"deleted"),Yt("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Cp(t){let n=(await pe()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Nt.join(xp,"mock-archive",t.id),o=aa(t.cwd),s=Nt.join(r,".bostrat","mocks");await St.mkdir(s,{recursive:!0});let a=await St.readdir(o).catch(()=>[]);for(let i of a)await St.copyFile(Nt.join(o,i),Nt.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 St.stat(Nt.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(),tn(i)}return await fe(),n.length}async function Np(t){let e=await pe(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await fe();for(let r of n)Yt("mocks",r.id).catch(()=>{})}return n.length}async function Pp(){if(!Tt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Zt("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await pe();for(let r of n.mocks){await tn(r);for(let o of r.versions||[])o.htmlRel&&await $n(r,o.v,"html"),o.pngRel&&await $n(r,o.v,"png")}return{pushed:n.mocks.length,source:"plane"}}import _o from"node:fs/promises";import Pt from"node:path";import sv from"node:os";import av from"node:net";import{randomUUID as iv}from"node:crypto";import{execFile as cv}from"node:child_process";import{promisify as lv}from"node:util";var he=lv(cv),uv=process.env.BOSTRAT_STATE_DIR||Pt.join(process.env.HOME||sv.tmpdir(),".bostrat"),Fp=Pt.join(uv,"views.json"),[Bp,Dp]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),nn=Number(process.env.BOSTRAT_VIEW_MAX)||3,dv="bostrat-view-",gr=t=>`${dv}${t}`,jp=new Set(["bostrat","www","api","views","app"]),ia=t=>Object.assign(new Error(t),{status:400}),fv=new Set(["npm","pnpm","yarn","bun"]),vc="package.json",pv=/[|;&`]|\$\(/,mv=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),hv=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Up=t=>String(t).trim().split(/\s+/).filter(Boolean),wv=/^[A-Za-z_][A-Za-z0-9_]*=/;function gv(t){for(let e of Up(t))if(!wv.test(e))return e;return null}function Hp(t){let e=Up(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function yv(t,e){let n=Hp(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Pt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Pt.resolve(t,n[r+1])}return t}var bv=t=>_o.stat(t).then(e=>e.isDirectory(),()=>!1),Wp=t=>_o.stat(t).then(e=>e.isFile(),()=>!1),ca=t=>_o.realpath(t).catch(()=>Pt.resolve(t));async function kv(t,e){if(Pt.resolve(t)===Pt.resolve(e))return!0;let[n,r]=await Promise.all([ca(t),ca(e)]);return n===r}async function xv(t){let n=(await _o.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 Wp(Pt.join(t,o.name,vc))?`./${o.name}`:null))).filter(Boolean)}async function Sv(t,e){let[n,r]=await Promise.all([ca(t),ca(e)]),o=Pt.relative(n,r);if(o&&(o.startsWith("..")||Pt.isAbsolute(o)))throw ia(`serve directory escapes the Track workspace: ${e}`)}async function vv(t,e){if(!await bv(t))throw ia(`serve directory does not exist: ${t}`);let n=gv(e);if(!n)return;if(!n.includes("/")&&!await Xi(n,{loginShell:!0,failOpen:!0}))throw ia(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(pv.test(e)||mv.has(n)||!fv.has(n)||!Hp(e).includes("run")||hv.test(e))return;let r=yv(t,e);if(await Wp(Pt.join(r,vc)))return;let o=await xv(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw ia(`no ${vc} in ${r}${s}`)}var me=null,xc=null;async function Oe(){return me||(xc||=(async()=>{try{me=JSON.parse(await _o.readFile(Fp,"utf8"))}catch{me={views:[],adoptWatermarks:{}}}finally{xc=null}return me.views||=[],me.adoptWatermarks||={},me})(),xc)}async function we(){await Ct(Fp,me)}function $e(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var Eo=new Map,Ec=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,qp=12e3;function _c(t){return Eo.get(t)||null}function Kp(t,{name:e,box:n}={}){let r=Eo.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 Eo.set(t,o),o}function Tc(t,e){let n=Eo.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function la(t,{error:e=null}={}){let n=Eo.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Gp(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 Ac(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function Tv(t,e){let n=Ac(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!jp.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!jp.has(s))return s}}function Io(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=av.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 Ic(t){if(process.platform!=="darwin"){await he("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await he("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 zp(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function Av(t,e){let n=new Set(t.views.map(o=>o.port)),r=zp();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=Bp;o<=Dp;o++)if(!(n.has(o)||r.has(o))&&!await Io(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Bp}-${Dp}`)}async function Ev(t){try{let{stdout:e}=await he(N,B("list-sessions","-F","#{session_name}"));return e.split(`
77
- `).some(n=>n.trim()===gr(t))}catch{return!1}}async function Ro(t){try{await he(N,B("kill-session","-t",gr(t)))}catch{}}async function Rc(t){if(!await ho())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=gr(t.slug),n=t.serveCwd??t.cwd,r=Pt.dirname(process.execPath);await he(N,B("new-session","-d","-s",e,"-c",n)),await he(N,B("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await he(N,B("send-keys","-t",e,"Enter")),await he(N,B("send-keys","-t",e,"-l",t.cmd)),await he(N,B("send-keys","-t",e,"Enter"))}async function Lp(t,e=12){try{let{stdout:n}=await he(N,B("capture-pane","-t",gr(t),"-p"));return String(n||"").trimEnd().split(`
76
+ `)}`:e),this.name="MockRenderError"}},$n=null,kc=null;async function fe(){return $n||(kc||=(async()=>{try{$n=JSON.parse(await kt.readFile(Op,"utf8"))}catch{$n={mocks:[]}}finally{kc=null}return $n.mocks||=[],$n})(),kc)}async function de(){await Mt(Op,$n)}var tn=t=>Rt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),On=(t,e,n)=>ze("mocks",t.id,e,n,Ht(t,e)[n]).catch(()=>{});function ee(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 sa=t=>Pt.join(t,".bostrat","mocks");function Ht(t,e){let n=sa(t.cwd);return{html:Pt.join(n,`${t.id}.v${e}.html`),png:Pt.join(n,`${t.id}.v${e}.png`)}}function _e(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 Sc(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var uv=Buffer.from([137,80,78,71,13,10,26,10]);function Mp(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(uv)}async function dv(t){let e=Buffer.alloc(24),n=await kt.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function fv({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 pv(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,g)=>{r.onload=m,r.onerror=()=>g(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let g=m*o*4,y=0;y<o;y++,g+=4)Math.abs(c[g]-l[0])+Math.abs(c[g+1]-l[1])+Math.abs(c[g+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 mv(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 hv({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await nv.launch({executablePath:ov,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new te("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:hr})).newPage();a.setDefaultTimeout(wr),await a.goto(`file://${t}`,{waitUntil:"load",timeout:wr}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:wr});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await pv(a,i),u=l&&fv({...l,pad:iv*hr,align:hr});u&&(i=await mv(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Cp)throw new te(`capture produced a ${i.length}-byte image`);let c=Sc(i);if(!c)throw new te("output is not a PNG");return await kt.writeFile(e,i),{width:Math.round(c.width/hr),height:Math.round(c.height/hr),dpr:hr,renderMs:Date.now()-r}}catch(s){throw s instanceof te?s:new te(/timeout/i.test(s?.message||"")?`render timed out after ${wr}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function Ip({htmlPath:t,outPath:e,width:n}){let r=await kt.mkdtemp(Pt.join(vo.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Rp(sv,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:wr,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new te(c?.killed?`render timed out after ${wr}ms`:"firefox exited non-zero",s)}let a=await kt.stat(e).catch(()=>null);if(!a||a.size<Cp)throw new te(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Sc(await dv(e));if(!i)throw new te("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await kt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function wv(t){if(rv==="firefox")return Ip(t);try{return await hv(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return Ip(t);throw e}}var gv=wv;var xc=Promise.resolve();function Np(t){return xc=xc.then(t,t),xc}async function Pp(t,e){let n=await fe(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await de(),await ee(r,"rendering",{version:e});try{let o=await lv();if(o<cv)throw new te(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Ht(r,e),a=await gv({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await kt.rm(s.png,{force:!0}).catch(()=>{});return}Bp(r,{v:e,source:"html",htmlRel:Pt.relative(r.cwd,s.html),pngRel:Pt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await de(),await ee(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),tn(r),On(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await de(),await ee(r,"error",{version:e,error:r.error}),tn(r)}}}function Bp(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 yv({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?av:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Cn({trackId:t,group:e}={}){let n=await fe(),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 en(t){let n=(await fe()).mocks.find(r=>r.id===t);return n?{...n}:null}async function Dp({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=yv({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&&!Mp(r))throw new Error("image must be a PNG");let d=await fe(),p={id:QS().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 kt.mkdir(sa(o),{recursive:!0}),d.mocks.push(p),f?(await kt.writeFile(Ht(p,1).html,n),await de(),await ee(p,"created"),tn(p),On(p,1,"html"),Np(()=>Pp(p.id,1))):(await Lp(p,1,r),await de(),await ee(p,"created"),await ee(p,"ready",{version:1,width:p.width,height:p.height,dpr:p.dpr}),tn(p),On(p,1,"png")),{...p}}async function jp(t,{html:e,image:n,width:r}={}){let s=(await fe()).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&&!Mp(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 kt.mkdir(sa(s.cwd),{recursive:!0}),a?(s.state="queued",await kt.writeFile(Ht(s,i).html,e),await de(),await ee(s,"updated",{version:i}),tn(s),On(s,i,"html"),Np(()=>Pp(s.id,i))):(await Lp(s,i,n),await de(),await ee(s,"updated",{version:i}),await ee(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr}),tn(s),On(s,i,"png")),{...s}}async function Lp(t,e,n){let r=Sc(n);if(!r)throw new Error("image must be a PNG");let o=Ht(t,e);await kt.writeFile(o.png,n),Bp(t,{v:e,source:"image",htmlRel:null,pngRel:Pt.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Fp(t){let e=await fe(),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 ee(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Up(t){let e=await fe(),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=Ht(r,o);await kt.rm(s.html,{force:!0}).catch(()=>{}),await kt.rm(s.png,{force:!0}).catch(()=>{})}return await de(),await ee(r,"deleted"),Qt("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Hp(t){let n=(await fe()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Pt.join($p,"mock-archive",t.id),o=sa(t.cwd),s=Pt.join(r,".bostrat","mocks");await kt.mkdir(s,{recursive:!0});let a=await kt.readdir(o).catch(()=>[]);for(let i of a)await kt.copyFile(Pt.join(o,i),Pt.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 kt.stat(Pt.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(),tn(i)}return await de(),n.length}async function Wp(t){let e=await fe(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await de();for(let r of n)Qt("mocks",r.id).catch(()=>{})}return n.length}async function qp(){if(!ft())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await vt("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await fe();for(let r of n.mocks){await tn(r);for(let o of r.versions||[])o.htmlRel&&await On(r,o.v,"html"),o.pngRel&&await On(r,o.v,"png")}return{pushed:n.mocks.length,source:"plane"}}import Ao from"node:fs/promises";import Bt from"node:path";import bv from"node:os";import kv from"node:net";import{randomUUID as xv}from"node:crypto";import{execFile as Sv}from"node:child_process";import{promisify as vv}from"node:util";var me=vv(Sv),Tv=process.env.BOSTRAT_STATE_DIR||Bt.join(process.env.HOME||bv.tmpdir(),".bostrat"),Jp=Bt.join(Tv,"views.json"),[Kp,Gp]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),nn=Number(process.env.BOSTRAT_VIEW_MAX)||3,Av="bostrat-view-",gr=t=>`${Av}${t}`,Vp=new Set(["bostrat","www","api","views","app"]),aa=t=>Object.assign(new Error(t),{status:400}),Ev=new Set(["npm","pnpm","yarn","bun"]),Ac="package.json",_v=/[|;&`]|\$\(/,Iv=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Rv=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Xp=t=>String(t).trim().split(/\s+/).filter(Boolean),$v=/^[A-Za-z_][A-Za-z0-9_]*=/;function Ov(t){for(let e of Xp(t))if(!$v.test(e))return e;return null}function Yp(t){let e=Xp(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function Cv(t,e){let n=Yp(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Bt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Bt.resolve(t,n[r+1])}return t}var Mv=t=>Ao.stat(t).then(e=>e.isDirectory(),()=>!1),Zp=t=>Ao.stat(t).then(e=>e.isFile(),()=>!1),ia=t=>Ao.realpath(t).catch(()=>Bt.resolve(t));async function Nv(t,e){if(Bt.resolve(t)===Bt.resolve(e))return!0;let[n,r]=await Promise.all([ia(t),ia(e)]);return n===r}async function Pv(t){let n=(await Ao.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 Zp(Bt.join(t,o.name,Ac))?`./${o.name}`:null))).filter(Boolean)}async function Bv(t,e){let[n,r]=await Promise.all([ia(t),ia(e)]),o=Bt.relative(n,r);if(o&&(o.startsWith("..")||Bt.isAbsolute(o)))throw aa(`serve directory escapes the Track workspace: ${e}`)}async function Dv(t,e){if(!await Mv(t))throw aa(`serve directory does not exist: ${t}`);let n=Ov(e);if(!n)return;if(!n.includes("/")&&!await ec(n,{loginShell:!0,failOpen:!0}))throw aa(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(_v.test(e)||Iv.has(n)||!Ev.has(n)||!Yp(e).includes("run")||Rv.test(e))return;let r=Cv(t,e);if(await Zp(Bt.join(r,Ac)))return;let o=await Pv(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw aa(`no ${Ac} in ${r}${s}`)}var pe=null,vc=null;async function $e(){return pe||(vc||=(async()=>{try{pe=JSON.parse(await Ao.readFile(Jp,"utf8"))}catch{pe={views:[],adoptWatermarks:{}}}finally{vc=null}return pe.views||=[],pe.adoptWatermarks||={},pe})(),vc)}async function he(){await Mt(Jp,pe)}function Re(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 To=new Map,Ic=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Qp=12e3;function Rc(t){return To.get(t)||null}function tm(t,{name:e,box:n}={}){let r=To.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 To.set(t,o),o}function Ec(t,e){let n=To.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function ca(t,{error:e=null}={}){let n=To.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function em(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 _c(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function jv(t,e){let n=_c(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Vp.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Vp.has(s))return s}}function Eo(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=kv.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 $c(t){if(process.platform!=="darwin"){await me("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await me("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 nm(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function Lv(t,e){let n=new Set(t.views.map(o=>o.port)),r=nm();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=Kp;o<=Gp;o++)if(!(n.has(o)||r.has(o))&&!await Eo(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Kp}-${Gp}`)}async function Fv(t){try{let{stdout:e}=await me(N,P("list-sessions","-F","#{session_name}"));return e.split(`
77
+ `).some(n=>n.trim()===gr(t))}catch{return!1}}async function _o(t){try{await me(N,P("kill-session","-t",gr(t)))}catch{}}async function Oc(t){if(!await wo())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=gr(t.slug),n=t.serveCwd??t.cwd,r=Bt.dirname(process.execPath);await me(N,P("new-session","-d","-s",e,"-c",n)),await me(N,P("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await me(N,P("send-keys","-t",e,"Enter")),await me(N,P("send-keys","-t",e,"-l",t.cmd)),await me(N,P("send-keys","-t",e,"Enter"))}async function zp(t,e=12){try{let{stdout:n}=await me(N,P("capture-pane","-t",gr(t),"-p"));return String(n||"").trimEnd().split(`
78
78
  `).filter(Boolean).slice(-e).join(`
79
- `)}catch{return""}}var Re=new Map,_v=12e4;async function Iv(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await Ev(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Lp(t.slug)||"server exited before listening"),Re.delete(t.id),await $e(t,t.state,t.error?{error:t.error}:{}),!0}if(await Io(t.port))return Re.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await $e(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>_v?(t.state="error",t.error=await Lp(t.slug)||"never started listening",await $e(t,"error",{error:t.error}),!0):!1;let o=(Re.get(t.id)||0)+1;return Re.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var Sc=!1;async function ua(){if(!Sc){Sc=!0;try{let t=await Oe(),e=!1;for(let n of t.views)await Iv(n)&&(e=!0);e&&await we()}finally{Sc=!1}}}async function da({trackId:t}={}){await ua();let e=await Oe();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function Vp(t){let n=(await Oe()).views.find(r=>r.id===t);return n?{...n}:null}function $c(t){return me&&me.views.find(e=>e.slug===t)||null}async function Oc({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=Pt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await Sv(r,u),await vv(u,n)},d=await Oe();if(i!=="detected"){let w=Ac(e),g=String(n).trim(),b=d.views.find(S=>S.trackId===t&&(S.slug===w||Ac(S.name)===w||S.cmd===g&&Pt.resolve(S.serveCwd??S.cwd)===u));if(b)return Rv(d,b,{cmd:g,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>=nn)throw new Error(`view cap reached (${nn} live) \u2014 stop one first`);let h={id:iv().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:Tv(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await Av(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Rc(h),d.views.push(h),await we(),await $e(h,"created",{origin:i}),Tc(t,h.id),i==="detected"&&await Io(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await we(),await $e(h,"live")),{...h}}async function Rv(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 kv(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Io(e.port))return await we(),Tc(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>=nn)throw new Error(`view cap reached (${nn} live) \u2014 stop one first`);return e.origin==="detected"?(await Ic(e.port),e.origin="agent"):await Ro(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Re.delete(e.id),await Rc(e),await we(),await $e(e,"created",{restarted:!0,reused:!0}),Tc(e.trackId,e.id),{...e,reused:!0}}async function Jp(t){let n=(await Oe()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await Ic(n.port):await Ro(n.slug),n.state!=="stopped"&&(n.state="stopped",Re.delete(n.id),await we(),await $e(n,"stopped")),{...n}}async function Xp(t){let e=await Oe(),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>=nn)throw new Error(`view cap reached (${nn} live) \u2014 stop one first`);return await Ro(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Re.delete(n.id),await Rc(n),await we(),await $e(n,"created",{restarted:!0}),{...n}}async function Yp(t){let e=await Oe(),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 Ic(r.port):await Ro(r.slug),e.views.splice(n,1),Re.delete(r.id),await we(),await $e(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function Zp(t){let e=await Oe(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Ro(r.slug),Re.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await we()),n.length}var $v=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function Qp(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await Oe(),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 we()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll($v)){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(!zp().has(c)&&!n.views.some(l=>l.port===c)&&await Io(c))try{i.push(await Oc({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 Ne from"node:fs/promises";import Pe from"node:path";import Ov from"node:os";import{randomUUID as Mv}from"node:crypto";var Mn=process.env.BOSTRAT_STATE_DIR||Pe.join(process.env.HOME||Ov.tmpdir(),".bostrat"),em=Pe.join(Mn,"runbooks.json"),fa=Pe.join(Mn,"runbooks"),tm=256*1024,Cv=[{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 $o(t){return Cv.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Me=null,Mc=null;async function Be(){return Me||(Mc||=(async()=>{let t=!0;try{Me=JSON.parse(await Ne.readFile(em,"utf8"))}catch{Me={runbooks:[]},t=!1}finally{Mc=null}return Me.runbooks||=[],!t&&Tt()&&await Nv().catch(()=>{}),Me})(),Mc)}async function Cn(){await Ct(em,Me)}var Oo=t=>Promise.resolve(t).catch(()=>{});function pa(t){return Oo(de("runbooks",{...t},{trackId:t.trackId}))}async function Nv(){let{records:t,source:e}=await Zt("runbooks");if(!(e!=="plane"||!t.length)){await Ne.mkdir(fa,{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 yp("runbooks",r.id,o.v,"md");typeof s=="string"&&await Ne.writeFile(Ce(r,o.v),s).catch(()=>{})}Me.runbooks.push(r)}Me.runbooks.length&&await Cn()}}function yr(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function Ce(t,e){return Pe.join(fa,`${t.id}.v${e}.md`)}function nm(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 rm(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>tm)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${tm/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=$o(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 om(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function ma({trackId:t,status:e}={}){let n=await Be(),r=t?n.runbooks.filter(o=>o.trackId===t):n.runbooks;return e&&(r=r.filter(o=>o.status===e)),r.map(o=>({...o}))}async function Cc(t){let n=(await Be()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function sm({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=rm(r),c=await Be(),l={id:Mv().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 Ne.mkdir(fa,{recursive:!0}),await Ne.writeFile(Ce(l,1),r),l.versions.push({v:1,mdRel:Pe.relative(Mn,Ce(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Cn(),await yr(l,"created",{version:1}),pa(l),Oo(mr("runbooks",l.id,1,"md",Ce(l,1))),{...l}}async function am(t,{md:e,note:n}={}){let o=(await Be()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");om(o,"update");let s=rm(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Ne.mkdir(fa,{recursive:!0}),await Ne.writeFile(Ce(o,a),e),o.versions.push({v:a,mdRel:Pe.relative(Mn,Ce(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Cn(),await yr(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),pa(o),Oo(mr("runbooks",o.id,a,"md",Ce(o,a))),{...o}}async function im(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Be()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return om(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Cn(),await yr(r,e==="done"?"done":"reopened"),pa(r),{...r})}async function cm(t){let e=await Be(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await Ne.rm(Pe.join(Mn,o.mdRel),{force:!0}).catch(()=>{});return await Cn(),r.trackDeletedAt||await yr(r,"deleted"),Oo(Yt("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function lm(t){let n=(await Be()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await yr(r,"orphaned"),r.cwd=null,pa(r);return n.length&&await Cn(),n.length}async function um(t){let e=await Be(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await yr(r,"deleted");for(let o of r.versions||[])await Ne.rm(Pe.join(Mn,o.mdRel),{force:!0}).catch(()=>{});Oo(Yt("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Cn()),n.length}async function dm(){if(!Tt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Zt("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Be();for(let r of n.runbooks){await de("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await mr("runbooks",r.id,o.v,"md",Pe.join(Mn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import No from"node:fs/promises";import Am from"node:path";import uT from"node:os";import{randomUUID as Em}from"node:crypto";import Pv from"node:fs/promises";import fm from"node:path";import Bv from"node:os";var pm=()=>fm.join(process.env.BOSTRAT_STATE_DIR||fm.join(process.env.HOME||Bv.tmpdir(),".bostrat"),"integrations.json");async function De(){try{return JSON.parse(await Pv.readFile(pm(),"utf8"))||{}}catch{return{}}}var ha=["linked","prOpened","prMerged","create"],Nc=["prompt","auto","off"],Dv={create:["prompt","off"]},mm=t=>Dv[t]??Nc;function wa(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of ha){let s=mm(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 Nn(){let t=await De(),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:wa(t),defaultTeam:n}:null}async function ga(){let t=await De(),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:wa(t)}}async function Pc(){return wa(await De())}async function hm(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${ha.join(", ")}`);for(let[r,o]of e){if(!ha.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${ha.join(", ")}`);let s=mm(r);if(!s.includes(o))throw new Error(s.length<Nc.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Nc.join(", ")}`)}let n=await De();return n.ticketFlow={...wa(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Ct(pm(),n,{mode:384}),n.ticketFlow}async function Mo(){let t=(await De()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function jv(){let t=(await De()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var Lv={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function ya(){let e=(await De()).voiceChat||{},n=e.provider||"openai",r=Lv[n];if(!r)return null;let o=n==="openai"?await jv():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function wm(){let t=(await De()).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 Pn(){let t=(await De()).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 Dc={};bd(Dc,{createComment:()=>Vv,findIssueUrl:()=>Wv,issueStates:()=>Yv,mdToAdf:()=>bm,moveIssue:()=>Zv,parseIssueRef:()=>Hv,resolveIssue:()=>qv,uploadImage:()=>Jv,upsertCard:()=>Kv,whoami:()=>Bc});var br=t=>String(t.baseUrl||"").replace(/\/$/,""),ym=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Bn(t,e,n,r){let o=await fetch(`${br(t)}${n}`,{method:e,headers:{authorization:ym(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 Fv=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,Uv=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function Hv(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(Fv);return r?r[1].toUpperCase():null}let n=e.match(Uv);return n?n[1]:null}function Wv(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 qv(t,e){let n;try{n=await Bn(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:`${br(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function Kv(t,e,{url:n,title:r,subtitle:o}){await Bn(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 Gv=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function zv(t){let e=[],n=0;for(let r of t.matchAll(Gv)){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 bm(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(...zv(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 Vv(t,e,n){let r=await Bn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:bm(n)});return r?.id?`${br(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function Bc(t){let e=await Bn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(br(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function Jv(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(`${br(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:ym(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??`${br(t)}/rest/api/3/attachment/content/${c.id}`}var Xv={new:"unstarted",indeterminate:"started",done:"completed"},gm=t=>Xv[t?.key]??"started";async function Yv(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([Bn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),Bn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:gm(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:gm(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function Zv(t,e,n){await Bn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var Uc={};bd(Uc,{createComment:()=>aT,createIssue:()=>Fc,createProject:()=>Lc,findIssueUrl:()=>rT,issueStates:()=>cT,moveIssue:()=>lT,parseIssueRef:()=>nT,resolveIssue:()=>oT,teams:()=>kr,uploadImage:()=>iT,upsertCard:()=>sT,whoami:()=>jc});var Qv=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function ge(t,e,n={}){let r=await fetch(Qv(),{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 tT=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,eT=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function nT(t){let e=String(t||"").trim(),n=e.match(tT);if(n)return n[1].toUpperCase();let r=e.match(eT);return r?r[1]:null}function rT(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 oT(t,e){let r=(await ge(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 sT(t,e,{url:n,title:r,subtitle:o}){let s=await ge(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 aT(t,e,n){let r=await ge(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 jc(t){let e=await ge(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 iT(t,{filename:e,contentType:n,buffer:r}){let o=await ge(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
79
+ `)}catch{return""}}var Ie=new Map,Uv=12e4;async function Hv(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await Fv(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await zp(t.slug)||"server exited before listening"),Ie.delete(t.id),await Re(t,t.state,t.error?{error:t.error}:{}),!0}if(await Eo(t.port))return Ie.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await Re(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>Uv?(t.state="error",t.error=await zp(t.slug)||"never started listening",await Re(t,"error",{error:t.error}),!0):!1;let o=(Ie.get(t.id)||0)+1;return Ie.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var Tc=!1;async function la(){if(!Tc){Tc=!0;try{let t=await $e(),e=!1;for(let n of t.views)await Hv(n)&&(e=!0);e&&await he()}finally{Tc=!1}}}async function ua({trackId:t}={}){await la();let e=await $e();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function rm(t){let n=(await $e()).views.find(r=>r.id===t);return n?{...n}:null}function Cc(t){return pe&&pe.views.find(e=>e.slug===t)||null}async function Mc({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=Bt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await Bv(r,u),await Dv(u,n)},d=await $e();if(i!=="detected"){let g=_c(e),y=String(n).trim(),b=d.views.find(S=>S.trackId===t&&(S.slug===g||_c(S.name)===g||S.cmd===y&&Bt.resolve(S.serveCwd??S.cwd)===u));if(b)return Wv(d,b,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(g=>g.state==="live"||g.state==="starting").length>=nn)throw new Error(`view cap reached (${nn} live) \u2014 stop one first`);let m={id:xv().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:jv(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await Lv(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Oc(m),d.views.push(m),await he(),await Re(m,"created",{origin:i}),Ec(t,m.id),i==="detected"&&await Eo(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await he(),await Re(m,"live")),{...m}}async function Wv(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 Nv(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Eo(e.port))return await he(),Ec(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>=nn)throw new Error(`view cap reached (${nn} live) \u2014 stop one first`);return e.origin==="detected"?(await $c(e.port),e.origin="agent"):await _o(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Ie.delete(e.id),await Oc(e),await he(),await Re(e,"created",{restarted:!0,reused:!0}),Ec(e.trackId,e.id),{...e,reused:!0}}async function om(t){let n=(await $e()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await $c(n.port):await _o(n.slug),n.state!=="stopped"&&(n.state="stopped",Ie.delete(n.id),await he(),await Re(n,"stopped")),{...n}}async function sm(t){let e=await $e(),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>=nn)throw new Error(`view cap reached (${nn} live) \u2014 stop one first`);return await _o(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Ie.delete(n.id),await Oc(n),await he(),await Re(n,"created",{restarted:!0}),{...n}}async function am(t){let e=await $e(),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 $c(r.port):await _o(r.slug),e.views.splice(n,1),Ie.delete(r.id),await he(),await Re(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function im(t){let e=await $e(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await _o(r.slug),Ie.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await he()),n.length}var qv=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function cm(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await $e(),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 he()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(qv)){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(!nm().has(c)&&!n.views.some(l=>l.port===c)&&await Eo(c))try{i.push(await Mc({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 Me from"node:fs/promises";import Ne from"node:path";import Kv from"node:os";import{randomUUID as Gv}from"node:crypto";var Mn=process.env.BOSTRAT_STATE_DIR||Ne.join(process.env.HOME||Kv.tmpdir(),".bostrat"),um=Ne.join(Mn,"runbooks.json"),da=Ne.join(Mn,"runbooks"),lm=256*1024,Vv=[{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 Io(t){return Vv.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Oe=null,Nc=null;async function Pe(){return Oe||(Nc||=(async()=>{let t=!0;try{Oe=JSON.parse(await Me.readFile(um,"utf8"))}catch{Oe={runbooks:[]},t=!1}finally{Nc=null}return Oe.runbooks||=[],!t&&ft()&&await zv().catch(()=>{}),Oe})(),Nc)}async function Nn(){await Mt(um,Oe)}var Ro=t=>Promise.resolve(t).catch(()=>{});function fa(t){return Ro(Rt("runbooks",{...t},{trackId:t.trackId}))}async function zv(){let{records:t,source:e}=await vt("runbooks");if(!(e!=="plane"||!t.length)){await Me.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 Bs("runbooks",r.id,o.v,"md");typeof s=="string"&&await Me.writeFile(Ce(r,o.v),s).catch(()=>{})}Oe.runbooks.push(r)}Oe.runbooks.length&&await Nn()}}function yr(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 Ce(t,e){return Ne.join(da,`${t.id}.v${e}.md`)}function dm(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 fm(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>lm)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${lm/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Io(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 pm(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 Pe(),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 Pc(t){let n=(await Pe()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function mm({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=fm(r),c=await Pe(),l={id:Gv().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 Me.mkdir(da,{recursive:!0}),await Me.writeFile(Ce(l,1),r),l.versions.push({v:1,mdRel:Ne.relative(Mn,Ce(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Nn(),await yr(l,"created",{version:1}),fa(l),Ro(ze("runbooks",l.id,1,"md",Ce(l,1))),{...l}}async function hm(t,{md:e,note:n}={}){let o=(await Pe()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");pm(o,"update");let s=fm(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Me.mkdir(da,{recursive:!0}),await Me.writeFile(Ce(o,a),e),o.versions.push({v:a,mdRel:Ne.relative(Mn,Ce(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Nn(),await yr(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),fa(o),Ro(ze("runbooks",o.id,a,"md",Ce(o,a))),{...o}}async function wm(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Pe()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return pm(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Nn(),await yr(r,e==="done"?"done":"reopened"),fa(r),{...r})}async function gm(t){let e=await Pe(),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 Me.rm(Ne.join(Mn,o.mdRel),{force:!0}).catch(()=>{});return await Nn(),r.trackDeletedAt||await yr(r,"deleted"),Ro(Qt("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function ym(t){let n=(await Pe()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await yr(r,"orphaned"),r.cwd=null,fa(r);return n.length&&await Nn(),n.length}async function bm(t){let e=await Pe(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await yr(r,"deleted");for(let o of r.versions||[])await Me.rm(Ne.join(Mn,o.mdRel),{force:!0}).catch(()=>{});Ro(Qt("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Nn()),n.length}async function km(){if(!ft())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await vt("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Pe();for(let r of n.runbooks){await Rt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await ze("runbooks",r.id,o.v,"md",Ne.join(Mn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import Co from"node:fs/promises";import Nm from"node:path";import TT from"node:os";import{randomUUID as Pm}from"node:crypto";import Jv from"node:fs/promises";import xm from"node:path";import Xv from"node:os";var Sm=()=>xm.join(process.env.BOSTRAT_STATE_DIR||xm.join(process.env.HOME||Xv.tmpdir(),".bostrat"),"integrations.json");async function Be(){try{return JSON.parse(await Jv.readFile(Sm(),"utf8"))||{}}catch{return{}}}var ma=["linked","prOpened","prMerged","create"],Bc=["prompt","auto","off"],Yv={create:["prompt","off"]},vm=t=>Yv[t]??Bc;function ha(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of ma){let s=vm(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 Pn(){let t=await Be(),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:ha(t),defaultTeam:n}:null}async function wa(){let t=await Be(),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:ha(t)}}async function Dc(){return ha(await Be())}async function Tm(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${ma.join(", ")}`);for(let[r,o]of e){if(!ma.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${ma.join(", ")}`);let s=vm(r);if(!s.includes(o))throw new Error(s.length<Bc.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Bc.join(", ")}`)}let n=await Be();return n.ticketFlow={...ha(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Mt(Sm(),n,{mode:384}),n.ticketFlow}async function $o(){let t=(await Be()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function Zv(){let t=(await Be()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var Qv={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function ga(){let e=(await Be()).voiceChat||{},n=e.provider||"openai",r=Qv[n];if(!r)return null;let o=n==="openai"?await Zv():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function Am(){let t=(await Be()).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 Bn(){let t=(await Be()).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 Lc={};Ed(Lc,{createComment:()=>cT,findIssueUrl:()=>rT,issueStates:()=>dT,mdToAdf:()=>Im,moveIssue:()=>fT,parseIssueRef:()=>nT,resolveIssue:()=>oT,uploadImage:()=>lT,upsertCard:()=>sT,whoami:()=>jc});var br=t=>String(t.baseUrl||"").replace(/\/$/,""),_m=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Dn(t,e,n,r){let o=await fetch(`${br(t)}${n}`,{method:e,headers:{authorization:_m(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 tT=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,eT=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function nT(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(tT);return r?r[1].toUpperCase():null}let n=e.match(eT);return n?n[1]:null}function rT(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 oT(t,e){let n;try{n=await Dn(t,"GET",`/rest/api/3/issue/${encodeURIComponent(e)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${e}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${br(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function sT(t,e,{url:n,title:r,subtitle:o}){await Dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var aT=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function iT(t){let e=[],n=0;for(let r of t.matchAll(aT)){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 Im(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(...iT(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 cT(t,e,n){let r=await Dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Im(n)});return r?.id?`${br(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function jc(t){let e=await Dn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(br(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function lT(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(`${br(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:_m(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??`${br(t)}/rest/api/3/attachment/content/${c.id}`}var uT={new:"unstarted",indeterminate:"started",done:"completed"},Em=t=>uT[t?.key]??"started";async function dT(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([Dn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),Dn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Em(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Em(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function fT(t,e,n){await Dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var Wc={};Ed(Wc,{createComment:()=>kT,createIssue:()=>Hc,createProject:()=>Uc,findIssueUrl:()=>gT,issueStates:()=>ST,moveIssue:()=>vT,parseIssueRef:()=>wT,resolveIssue:()=>yT,teams:()=>kr,uploadImage:()=>xT,upsertCard:()=>bT,whoami:()=>Fc});var pT=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function we(t,e,n={}){let r=await fetch(pT(),{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 mT=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,hT=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function wT(t){let e=String(t||"").trim(),n=e.match(mT);if(n)return n[1].toUpperCase();let r=e.match(hT);return r?r[1]:null}function gT(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 yT(t,e){let r=(await we(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 bT(t,e,{url:n,title:r,subtitle:o}){let s=await we(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 kT(t,e,n){let r=await we(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 Fc(t){let e=await we(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 xT(t,{filename:e,contentType:n,buffer:r}){let o=await we(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 cT(t,e){let n=await ge(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 lT(t,e,n){if(!(await ge(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 kr(t){return((await ge(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var km=255;async function Lc(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>km,a=s?`${o.slice(0,km-1).trimEnd()}\u2026`:o,i=await ge(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 Fc(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await ge(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 _m={linear:{api:Uc,config:Nn},jira:{api:Dc,config:ga}};async function Vc(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(_m)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function Dn(t){let e=_m[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function Jc(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var Im=()=>process.env.BOSTRAT_STATE_DIR||Am.join(process.env.HOME||uT.tmpdir(),".bostrat"),Kc=()=>Am.join(Im(),"tickets.json"),Rm=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),ba=t=>`${Rm()}/track/${t}`,rn=null,Hc=null,Wc=Promise.resolve();async function Bo(){return rn||(Hc??=No.readFile(Kc(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(rn=t,!e&&Tt()){let{records:n,source:r}=await Zt("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;rn[s]=i}Object.keys(rn).length&&await Gc().catch(()=>{})}}return rn}).finally(()=>Hc=null),Hc)}async function Gc(){let t=JSON.stringify({tickets:rn},null,2),e=`${Kc()}.${process.pid}.tmp`;await No.mkdir(Im(),{recursive:!0}),await No.writeFile(e,t),await No.rename(e,Kc())}async function on(t=null){if(Wc=Wc.then(Gc,Gc),await Wc.catch(()=>{}),!t)return;let e=rn?.[t];e?de("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):Yt("tickets",t).catch(()=>{})}async function ye(t){return(await Bo())[t]??null}async function ka(){return{...await Bo()}}async function $m(){return(await Vc()).length>0}async function xr(t,e,{source:n="api"}={}){let r=await Vc();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 Bo())[t.id]=c,await on(t.id),await $({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Po(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 Co(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Cm(t.id,c,"linked"),c}async function Xc(t){let e=await Bo();return e[t]?(delete e[t],await on(t),!0):!1}var zc=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Om(t,e){if(!t.track?.cwd)return null;let n=ue(await L({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function dT(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${zc(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${zc(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function fT(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(zc(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Po(t,e){let n=await Dn(e);if(!n)return;let r=await Om(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:ba(t),title:`Bostrat Track: ${e.track.name}`,subtitle:fT(e,r)})}function xa(t){let e=new URL(Rm());return`${e.protocol}//${t}.${e.host}/`}var pT={linked:"linked",opened:"prOpened",merged:"prMerged"};function mT(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 Mm(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await on(t),await $({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Cm(t,e,n){let r=await Dn(e);if(!r)return;let o=r.cfg.ticketFlow?.[pT[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=Jc(s,a),c=mT(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 Mm(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await $({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Em(),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 Nm(t){let e=await ye(t);if(!e)throw new Error("no ticket linked to this Track");let n=await Dn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=Jc(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await on(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 Yc(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await ye(t);if(!r)throw new Error("no ticket linked to this Track");let o=await Dn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Jc(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 on(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Mm(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Pm(t,e){let n=await ye(t);if(!n)throw new Error("no ticket linked to this Track");let r=await Dn(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await $({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function Bm(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 Yc(t.id,o.payload.to?.id,{mode:"offer"})),await $({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var xm=50;async function Dm(){let t=await Nn();if(!t)throw new Error("no ticketing integration configured");return kr(t)}function hT(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>xm)throw new Error(`too many issues (${n.length}); cap is ${xm} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function wT(t,e,n,r){let o=r??await kr(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 ye(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 jm(t,e){let n=await Nn();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=hT(e),o=await kr(n),s=await wT(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Em();return await $({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function Lm(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(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await $({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await Nn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await kr(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let h=await Lc(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await Fc(a,{teamId:c.id,title:h.title,description:h.description,projectId:f,priority:h.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:h.title,message:w?.message||String(w)})}await $({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await $({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(h=>h.kind==="issue"),m=null;return i.linkCurrentTrack&&d.length===1&&!await ye(t.id)&&(m=await xr(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:m?m.key:null}}async function Co(t,e){let n=await Dn(t);n&&await n.api.createComment(n.cfg,t,e)}async function gT(t,e,n){let r=await Dn(t);if(!r)return null;let o=await en(e);if(!o)return null;let s=Ie(o,n);if(!s)return null;let a=await No.readFile(Ut(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 Sm=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),qc=new Map;function yT(t,e){if(Sm()<=0)return Po(t,e);if(qc.has(t))return;let n=setTimeout(()=>{qc.delete(t),ye(t).then(r=>r?Po(t,r):null).catch(()=>{})},Sm());n.unref?.(),qc.set(t,n)}var vm=new Map;async function Tm(t,e,n){let r=vm.get(t)??0;Date.now()-r<600*1e3||(vm.set(t,Date.now()),await $({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function bT(t,{resolveTrack:e=null}={}){let n=await ye(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await Vc())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 xr(s,o,{source:"chat"})}catch(a){await Tm(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 on(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await on(t.trackId),await Po(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 on(t.trackId),await Po(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Om(n,t.trackId).catch(()=>null):null;await Co(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: ${dT(o)}.`:"")),await Cm(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await gT(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Co(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 Co(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 Co(n,`**Bostrat** \u2014 live preview **${r.name}**: ${xa(r.slug)}
90
- _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await yT(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Tm(t.trackId,s?.cwd??null,o)}}function Fm({resolveTrack:t}){zt.on("event",e=>{bT(e,{resolveTrack:t}).catch(()=>{})})}async function Um(){if(!Tt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Zt("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Bo(),r=0;for(let[o,s]of Object.entries(n))await de("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import Sa from"node:fs/promises";import Hm from"node:path";import kT from"node:os";var Wm=process.env.BOSTRAT_STATE_DIR||Hm.join(process.env.HOME||kT.tmpdir(),".bostrat"),Qc=Hm.join(Wm,"pr-links.json"),ft=(t,e)=>`${t}#${Number(e)}`,va=null,Zc=null,D=Promise.resolve();async function kt(){return va||(Zc??=Sa.readFile(Qc,"utf8").then(t=>JSON.parse(t)?.links??{}).catch(()=>({})).then(t=>va=t).finally(()=>Zc=null),Zc)}async function Z(){let t=JSON.stringify({links:va},null,2),e=`${Qc}.${process.pid}.tmp`;await Sa.mkdir(Wm,{recursive:!0}),await Sa.writeFile(e,t),await Sa.rename(e,Qc)}var xT=new Set(["api","observed","reconciled"]);async function Ht({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=ft(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:xT.has(a),firstSeen:Date.now()},D=D.then(Z,Z),await D.catch(()=>{}),{link:i[c],created:!0})}async function Do(t,e){if(!t||e==null)return null;let n=(await kt())[ft(t,e)];return!n||n.announced?null:(n.announced=!0,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Rt(t,e){return!t||e==null?null:(await kt())[ft(t,e)]??null}async function sn(t,e,n){if(!t||e==null)return null;let r=(await kt())[ft(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,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function qm(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await kt())[ft(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function Km(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await kt())[ft(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function Ta(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await kt())[ft(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,D=D.then(Z,Z),await D.catch(()=>{}),r)}async function Aa(t,e){if(!t||e==null)return null;let n=(await kt())[ft(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Ea(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await kt())[ft(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,D=D.then(Z,Z),await D.catch(()=>{}),r)}async function _a(t,e){if(!t||e==null)return null;let n=(await kt())[ft(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Gm(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await kt())[ft(t,e)];return r?(r.baseFoldAt=n,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function zm(t,e){if(!t||e==null)return null;let n=(await kt())[ft(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Vm(t,e,n){if(!t||e==null||!n)return null;let r=(await kt())[ft(t,e)];return r?(r.lastHeadSha=n,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function jo(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await kt())[ft(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Sr(t,e,n=!0){if(!t||e==null)return null;let r=(await kt())[ft(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Ia(t,e,n=!0){if(!t||e==null)return null;let r=(await kt())[ft(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Jm(t,e,n){if(!t||e==null)return null;let r=(await kt())[ft(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()],D=D.then(Z,Z),await D.catch(()=>{}),r}async function Lo(t,e){if(!t||e==null)return null;let n=(await kt())[ft(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,D=D.then(Z,Z),await D.catch(()=>{}),n)}var ST=new Set(["merged","closed","lost"]);async function Xm(){return Object.values(await kt()).filter(t=>!ST.has(t.state))}async function be(){return Object.values(await kt()).sort((t,e)=>e.firstSeen-t.firstSeen)}var Zm=t=>Math.round(t*100)/100;function vT(t){return!t||t.kind==="setup"?[]:Bt(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():Ym.basename(e.base)}))}function Qm(t){let e=new Map;for(let n of t||[])for(let r of vT(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function Ra(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 tl=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function TT(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 el(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,m]=f.split("#");return`${d.split("/").pop()}#${m}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(tl(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(tl(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(`${tl(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=ue(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?TT(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function AT(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 th(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 m=Ra(d);m&&(!i||m>i)&&(i=m),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:Zm(l),firstActivityAt:c,lastActivityAt:i}}async function eh(t){let e=Qm(t),n=await be().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,ue(c).totalUsd)}let s=[...e.values()].map(a=>th(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function nh(t,e){let r=Qm(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([be().catch(()=>[]),On().catch(()=>[]),da().catch(()=>[]),ma().catch(()=>[]),ka().catch(()=>({}))]),l=new Map,u=[],f=(d,m)=>(Ra(m)||m.createdAt||0)-(Ra(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let m=await L(d).catch(()=>[]),h=ue(m);l.set(d.id,h.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:AT(d),handoffs:(d.events||[]).filter(g=>g.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:Bt(d.workspace).map(g=>g.repo||Ym.basename(g.base||"")),prs:o.filter(g=>g.trackId===d.id&&(!r.slug||g.repo===r.slug)).map(g=>({repo:g.repo,number:g.number,state:g.state??null,title:g.title??null,url:g.url??null})),mocks:s.filter(g=>g.trackId===d.id).length,views:a.filter(g=>g.trackId===d.id).length,runbooks:i.filter(g=>g.trackId===d.id).map(g=>({name:g.name,status:g.status})),ticket:w?{provider:w.provider,key:w.key,url:w.url??null}:null,usd:Zm(h.totalUsd),lastActivityAt:Ra(d)})}return{...th(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as ET}from"node:child_process";import{promisify as _T}from"node:util";import{randomUUID as IT}from"node:crypto";var jn=_T(ET),$a=t=>new Promise(e=>setTimeout(e,t)),RT=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,rh=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,$T=250,OT=400,oh={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 ah(t){let e=await ef(t);if(!e)return!1;try{return await jn("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function nl(t,e){let n=Date.now()+e;for(;;){if(await ah(t))return!0;if(Date.now()>n)return!1;await $a($T)}}async function MT(t,e){let n=oh[e]||oh.default;n.escape&&(await xt(t,["Escape"],{kind:"swap-exit"}),await $a(150)),n.wipe&&await Fs(t,400,{kind:"swap-exit"}),await Us(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=_e(e).submitDelayMs||0;await $a(Math.max(r,250)),await xt(t,["Enter"],{kind:"swap-exit"})}async function CT(t,e){try{let{stdout:n}=await jn(N,B("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function sh(t){await jn(N,B("respawn-pane","-k","-t",t))}async function rl(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 Y(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||ar(n),a=Number(e.exitTimeoutMs)||RT;await G({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await ah(t)&&(await MT(t,o),!await nl(t,a)&&(i=!0,await G({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await sh(t),!await nl(t,rh))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&sr(n),l=c?Rs():null,u=await CT(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await jn(N,B("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await jn(N,B("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(g){throw l?Ds(g,l):g}if(f&&!i&&(await sh(t),!await nl(t,rh)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,m=e.resumeSessionId||IT(),h=d?n:Ji(n,m),w=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await Ge(r.cwd),s==="codex"&&await tr(r.cwd),s==="cursor"&&await er(r.cwd),await In(t),await jn(N,B("set-option","-t",t,"@bostrat-sid",m)),await jn(N,B("set-option","-t",t,"@bostrat-agent",s)),await $a(OT),await vt(t,h,{kind:"swap-launch"}),await G({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:m,forced:i,envRespawned:f}),{name:t,cwd:r.cwd,sessionId:m,agent:s,pinned:w,forced:i,envRespawned:f}}import fA from"node:fs/promises";function NT(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 Fo(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 Uo(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 Tr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,_t=t=>t&&!/[$`]/.test(t)?t:null,mt=t=>t.replace(/\/+$/,"").split("/").pop()||t,vr=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},lh=t=>t.startsWith("-"),ih=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function at(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(lh(o)){e.has(o)&&r++;continue}n.push(o)}return n}var PT={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 ${mt(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"},BT={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 uh(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 DT(t){let e=uh(t),n=_t(at(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${mt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${mt(n)}`:t.includes("-i")&&n?`Edit ${mt(n)}`:null}function ol(t){if(t.includes("--files")){let s=_t(at(t,ih)[0]);return s&&s!=="."?`List files in ${vr(s)}`:"List files"}let e=at(t,ih),n=e[0];if(!n)return null;let r=_t(e.slice(1).find(s=>s!==".")),o=Tr(n,40);return r?`Search for \u201C${o}\u201D in ${vr(r)}`:`Search for \u201C${o}\u201D`}function sl(t){let e=at(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 al(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=at(t)[0];return n?`Run ${mt(n)}`:null}function jT(t){let e=at(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 ch(t){return t.find(e=>/^https?:\/\//.test(e))||null}var LT={git:t=>{let e=at(t,new Set(["-C","-c"])),n=PT[e[0]];return n?n(e,t):null},gh:t=>{let e=at(t),n=BT[e[0]];return n?n(e):null},sed:DT,rg:ol,grep:ol,egrep:ol,nl:t=>{let e=_t(at(t)[0]);return e?`Read ${mt(e)}`:null},cat:t=>{let e=_t(at(t)[0]);return e?`Read ${mt(e)}`:null},head:t=>{let e=_t(at(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${mt(e)}`:null},tail:t=>{let e=_t(at(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${mt(e)}`:null},wc:t=>{let e=_t(at(t)[0]);return e?`Count lines in ${mt(e)}`:"Count input lines"},ls:t=>{let e=_t(at(t)[0]);return e&&e!=="."?`List files in ${vr(e)}`:"List files"},find:t=>_t(t[1])&&!lh(t[1])&&t[1]!=="."?`Find files in ${vr(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=_t(at(t)[0]);return e?`Create directory ${vr(e)}`:null},touch:t=>{let e=_t(at(t)[0]);return e?`Create ${mt(e)}`:null},rm:t=>{let e=_t(at(t)[0]);return e?`Delete ${vr(e)}`:null},mv:t=>{let e=_t(at(t)[0]);return e?`Move ${mt(e)}`:null},cp:t=>{let e=_t(at(t)[0]);return e?`Copy ${mt(e)}`:null},chmod:t=>{let e=_t(at(t)[1]);return e?`Change permissions on ${mt(e)}`:null},diff:t=>{let e=at(t);return e.length>=2?`Compare ${mt(e[0])} and ${mt(e[1])}`:null},curl:t=>{let e=ch(t);return e?`Fetch ${Tr(e,56)}`:"Fetch a URL"},wget:t=>{let e=ch(t);return e?`Fetch ${Tr(e,56)}`:"Fetch a URL"},npm:sl,pnpm:sl,yarn:sl,npx:t=>{let e=at(t)[0];return e?`Run ${e}`:null},node:t=>al(t,"Node"),python:t=>al(t,"Python"),python3:t=>al(t,"Python"),make:t=>{let e=at(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:jT,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=at(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=at(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=at(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},FT=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function UT(t){let e=Uo(Fo(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(mt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return il(e[2]);let n=mt(e[0]);if(FT.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=LT[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function il(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=NT(e);for(let i=0;i<r.length-1;i++){let c=Uo(Fo(r[i])),l=Uo(Fo(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(mt(c[0]))&&mt(l[0])==="sed"){let u=uh(l),f=at(c)[0];if(u&&f)return Tr(u.from===u.to?`Read line ${u.from} of ${mt(f)}`:`Read lines ${u.from}\u2013${u.to} of ${mt(f)}`,88)}}let o=[];for(let i of r){let c=UT(i);c&&o.push({d:c,head:mt(Uo(Fo(i))[0]||""),sub:at(Uo(Fo(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":Tr(s?s.d:o[0].d,88)}var ll=new Set(["Bash"]);function Ar(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 an(t,e=""){let n=e.trim();return n||il(t)||Ar(t)}function cl(t,e){if(t){if(Array.isArray(t)){for(let n of t)cl(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&ll.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))cl(n,e)}}}async function dh(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Mt(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(cl(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:an(f.command,f.description),ts:u})}return o.slice(-n)}function fh(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((h,w)=>w>=s&&(h.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let m=d.map(h=>({...h,n:i(h)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:m,ts:Math.max(...m.map(h=>h.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 HT=/^Async (?:agent|task) launched/i,WT=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 ph(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Mt(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 m=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(m.description||"").trim()||null,detail:String(m.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let m=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(m?Ar(m):null),detail:m?Ar(m):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&HT.test(WT(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 Ho from"node:fs";import KT from"node:fs/promises";import Ln from"node:path";import GT from"node:os";import zT from"node:crypto";import VT from"node:zlib";var Oa=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),ee=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Er=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function cn(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 ul=t=>`"${String(t).replace(/"/g,'\\"')}"`;function qT(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 mh(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:ee(`read ${cn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Er(r)}`:`Read ${Er(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?cn(e.path,n):null,s=e?.glob?` --glob ${ul(e.glob)}`:"",a=ee(String(r),40);return{command:ee(`grep ${ul(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?cn(e.target_directory,n):null;return{command:ee(`glob ${ul(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${ee(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:ee(`edit ${cn(e.path,n)}`,140),description:`Edit ${Er(e.path)}`}:null;case"Write":return e?.path?{command:ee(`write ${cn(e.path,n)}`,140),description:`Write ${Er(e.path)}`}:null;case"Delete":return e?.path?{command:ee(`delete ${cn(e.path,n)}`,140),description:`Delete ${Er(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>cn(s,n)).filter(Boolean);return{command:ee(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=qT(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:ee(`apply_patch ${cn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Er(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:ee(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var JT=process.env.BOSTRAT_CURSOR_CHATS||Ln.join(GT.homedir(),".cursor","chats");function gh(t){let e=zT.createHash("md5").update(String(t)).digest("hex");return Ln.join(JT,e)}function yh(t){try{let e=JSON.parse(Ho.readFileSync(Ln.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 XT(t){let e=gh(t),n;try{n=Ho.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Ln.join(e,o.name),a=Ln.join(s,"store.db");if(!Ho.existsSync(a))continue;let i=yh(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 Ma(t,e){if(!t)return null;if(e){let n=Ln.join(gh(t),e),r=Ln.join(n,"store.db");if(Ho.existsSync(r))return{id:e,dir:n,store:r,...yh(n)||{createdAtMs:null,updatedAtMs:null}}}return XT(t)}function hh(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function YT(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=VT.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function ZT(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 QT(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!Oa.has(r.toolName))continue;let o=mh(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 tA=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():"",eA=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],wh=process.getBuiltinModule?.("node:sqlite"),_r={turns:[],trailingCommands:[],model:null,requestIds:[]};async function dl(t,e){if(!wh)return _r;let{DatabaseSync:n}=wh,r;try{r=new n(t,{readOnly:!0})}catch{return _r}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 _r;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return _r}let a=s.lastUsedModel||null,i=s.latestRootBlobId?hh(r,s.latestRootBlobId):null;if(!i)return{..._r,model:a};let c=[],l=[],u=[],f=null,d=[];for(let m of ZT(i)){let h=YT(hh(r,m));if(!h)continue;let w=h.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),h.role!=="assistant")continue;if(Array.isArray(h.content))for(let S of h.content){let A=S?.providerOptions?.cursor?.modelName;A&&(f=A)}l.push(...QT(h.content,e).map((S,A)=>({...S,id:S.id||`${m}:${A}`}))),u.push(...eA(h.content));let g=tA(h.content);if(!g)continue;let b=[...new Set(u)];u=[],c.push({id:m,text:g,tools:b.length?b:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return _r}finally{try{r.close()}catch{}}}function bh(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(Ho.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 kh(t,e,{limit:n=80}={}){let r=Ma(t,e);if(!r)return[];let{turns:o,model:s}=await dl(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=bh(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 xh(t,e){let n=Ma(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await dl(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 Sh(t,e,{limit:n=80}={}){let r=Ma(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await dl(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=bh(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:an(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:an(u.command,u.description),ts:i+1});return l}async function vh(t,e){let n=Ma(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await KT.stat(n.store)).mtimeMs)}catch{return null}}import Th from"node:fs";import pl from"node:fs/promises";import fl from"node:path";import nA from"node:os";var rA=process.env.BOSTRAT_CODEX_SESSIONS||fl.join(process.env.CODEX_HOME||fl.join(nA.homedir(),".codex"),"sessions");async function Ah(t){try{return await pl.realpath(t)}catch{return t}}function oA(t=rA){let e=[],n=r=>{let o;try{o=Th.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=fl.join(r,s.name);if(s.isDirectory())n(a);else if(s.isFile()&&s.name.startsWith("rollout-")&&s.name.endsWith(".jsonl")){let i=/-([0-9a-f-]{36})\.jsonl$/i.exec(s.name);try{e.push({id:i?i[1]:null,file:a,mtimeMs:Th.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function sA(t,e=1048576){let n;try{n=await pl.open(t,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(e,65536)),o="",s=0;for(;;){let{bytesRead:a}=await n.read(r,0,r.length,s);if(!a)break;o+=r.toString("utf8",0,a);let i=o.indexOf(`
97
- `);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function aA(t){let e=await sA(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 Fn(t,e){if(!t&&!e)return null;let n=oA();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await Ah(t);for(let o of n){let s=await aA(o.file);if(s&&(s===t||await Ah(s)===r))return o}return null}var iA=new Set(["output_text","text"]),cA=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&iA.has(e.type)).map(e=>e.text).join(`
98
- `).trim():"";async function Eh(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Fn(t,e);if(!o)return[];let s;try{s=await Mt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
99
- `)){let m=d.trim();if(!m||m[0]!=="{")continue;let h;try{h=JSON.parse(m)}catch{continue}if((h.type==="turn_context"&&h.payload?.model||h.type==="session_meta"&&h.payload?.model)&&(i=h.payload.model),h.type==="event_msg"){let S=h.payload?.type;S==="task_started"?(l=!0,u=!1,f=h.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&h.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:hl,sourceId:h.payload?.turn_id||f||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i}));continue}if(h.type!=="response_item")continue;let w=h.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,ml.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let g=cA(w.content),b=c.length?c.slice():null;c=[],!(!g&&!b)&&a.push({role:"assistant",text:g,tools:b||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}var Wo=new Set(["exec_command","shell","exec"]),ml=new Set(["wait","write_stdin"]),hl="Codex ended the turn without a response \u2014 it hit an error the CLI shows only in the terminal (e.g. quota or billing). Check the Terminal tab.";function lA(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 uA(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 dA(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=uA(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function _h(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Fn(t,e);if(!o)return[];let s;try{s=await Mt(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"&&Wo.has(u.name)){let m;try{m=JSON.parse(u.arguments||"{}")}catch{continue}let h=lA(m);h&&(f=[h])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=dA(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((m,h)=>{a.push({id:d?h?`${d}:${h}`:d:null,tool:u.name,command:m,description:"",summary:an(m),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Ih(t,e){let n=await Fn(t,e);if(!n)return null;try{return Math.round((await pl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var Ir=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}},wl=class extends Ir{transcript(e){return!e?.cwd||!e?.sessionId?null:Ee(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?oc(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?dh(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?ph(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await fA.stat(n)).mtimeMs)}catch{return null}}},gl=class extends Ir{async readMessages(e,n){return kh(e.cwd,e.sessionId,n)}async readCommands(e,n){return Sh(e.cwd,e.sessionId,n)}async updatedAt(e){return vh(e.cwd,e.sessionId)}},yl=class extends Ir{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return Eh(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return _h(e.cwd,this.sessionId(e),n)}async updatedAt(e){return Ih(e.cwd,this.sessionId(e))}},pA=new Ir("unknown"),Rh=new Map;function bl(t){return Rh.set(t.agent,t),t}function qo(t){return Rh.get(t)||pA}bl(new wl("claude",ll));bl(new gl("cursor",Oa));bl(new yl("codex",Wo));var kl=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function xl(t,e={}){return(await qo(t.agent).readMessages(t,e)).map(kl(t))}async function $h(t,e={}){return(await qo(t.agent).readCommands(t,e)).map(kl(t))}async function Oh(t,e={}){return(await qo(t.agent).readJobs(t,e)).map(kl(t))}var Nh=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Tl=/Chat about this/i,Un=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Mh=/^(type something|chat about this|submit)\b/i,Hn=/^[─—-]{6,}\s*$/,Al=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function Wn(t){let e=t.search(Al);return(e>=0?t.slice(0,e):t).trim()}function mA(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function hA(t){let e=String(t).trim();return!!(!e||Hn.test(t)||Al.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function wA(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=gA(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function gA(t,e){let n=String(e).toLowerCase();if(n){let o=t.map((s,a)=>({i:a,label:s.label.toLowerCase()})).sort((s,a)=>a.label.length-s.label.length);for(let{i:s,label:a}of o){if(a.split(/\s+/).filter(l=>l.length>3).some(l=>n.includes(l)))return s;let c=a.slice(0,8).trim();if(c.length>=4&&n.includes(c))return s}}let r=t.findIndex(o=>!o.answered);return r>=0?r:t.length-1}function Ch(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Ph=/^\s*Switch model\?\s*$/m;function yA(t,e=""){let n=String(t);if(!Ph.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(Un);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&&!Hn.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:Wn(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 Bh=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,bA=/^\s*[─—▔-]{6,}\s*$/;function kA(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||!Bh.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(Un);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&&!(bA.test(n[c])||Hn.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Wn(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 xA(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(Un);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 Sl=/Resume from summary/i,SA=/^\s*This session is\b/;function vA(t,e=""){let n=String(t);if(!Sl.test(n))return null;let r=n.replace(/\r/g,"").split(`
105
- `),o=-1;for(let u=r.length-1;u>=0;u--)if(SA.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(Un);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&&!Hn.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>Sl.test(u.label)))return null;let i=s.map(u=>({label:Wn(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 qn=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function El(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(Un);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 Dh=/Choose the text style that looks best with your terminal/,jh=/Let's get started\./;function TA(t,e=""){let n=String(t);if(!Dh.test(n)||!jh.test(n)||qn.test(n))return null;let r=n.replace(/\r/g,"").split(`
106
- `),o=El(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:Wn(s.label),description:""}))}]}:null}var Lh=/Select login method:/;function AA(t,e=""){let n=String(t);if(!Lh.test(n)||qn.test(n))return null;let r=n.replace(/\r/g,"").split(`
107
- `),o=El(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Wn(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 _l=/Paste code here if prompted/,EA=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function _A(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!EA.test(o)||_l.test(t[r]))break;n+=o}return n}function IA(t,e=""){let n=String(t);if(!_l.test(n)||qn.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=_A(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 vl=/OAuth error:/,RA=/Press Enter to retry\./;function $A(t,e=""){let n=String(t);if(!vl.test(n)||!RA.test(n)||qn.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
109
- `)].reverse().find(a=>vl.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 Fh=/Quick safety check: Is this a project you created or one you trust/;function OA(t,e=""){let n=String(t);if(!Fh.test(n)||qn.test(n))return null;let r=n.replace(/\r/g,"").split(`
110
- `),o=El(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:Wn(s.label),description:""}))}]}:null}var Uh=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function MA(t,e=""){let n=String(t);if(qn.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(Uh);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(Hn.test(r[l])||Al.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 CA(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Tl.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 NA(t,e=""){let n=String(t);if(!Nh.test(n)||!Tl.test(n))return null;let r=n.replace(/\r/g,"").split(`
112
- `),o=CA(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let T=i;T<c;T++){let M=r[T].match(Un);M&&(u<0&&(u=T),l.push({line:T,cursor:!!M[1],checked:M[3]!=null&&M[3]!==" ",hasCheckbox:M[3]!=null,label:M[4].trim(),desc:""}))}if(!l.length)return null;for(let T of l)for(let M=T.line+1;M<Math.min(r.length,T.line+4)&&!(Un.test(r[M])||Hn.test(r[M]));M++){let W=r[M].trim();if(!(!W||hA(r[M])||Mh.test(W))){T.desc=mA(W);break}}let f=l.filter(T=>!Mh.test(T.label));if(!f.length)return null;let d=f.some(T=>T.hasCheckbox),m=Math.max(0,f.findIndex(T=>T.cursor)),h=s>=0?s+1:0,w=[];for(let T=h;T<u;T++){let M=r[T].trim();M&&!Hn.test(r[T])&&!/[☐☒]/.test(r[T])&&w.push(M)}let g=w.join(" ")||(s>=0?Ch(r[s]):"")||"The agent is asking a question",b=s>=0?wA(r[s],g):null,S=b?.currentTabLabel||(s>=0?Ch(r[s]):""),A=f.map(T=>({label:Wn(T.label),description:T.desc})),C=A.map(T=>T.label).join("|"),O=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${S}:${g}:${C}`.slice(0,240),F=b?`claude:ask:${b.tabs.map(T=>T.label).join("|")}`.slice(0,240):O;return{provider:"claude",box:e,multiSelect:d,cursorIndex:m,promptId:O,groupId:F,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:g,header:S,multiSelect:d,options:A}]}}function Rr(t,e){let n=String(e||"");return n?t==="claude"?Nh.test(n)&&Tl.test(n)||Ph.test(n)||Sl.test(n)||Bh.test(n.trimEnd().split(`
113
- `).pop()||"")?!0:qn.test(n)?!1:Dh.test(n)&&jh.test(n)||Lh.test(n)||_l.test(n)||vl.test(n)||Fh.test(n)||Uh.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 Kn(t,e){return!t||!e?null:t.agent==="claude"?xA(e,t.name)||NA(e,t.name)||yA(e,t.name)||vA(e,t.name)||$A(e,t.name)||IA(e,t.name)||AA(e,t.name)||TA(e,t.name)||OA(e,t.name)||MA(e,t.name)||kA(e,t.name):t.agent==="cursor"||t.agent==="codex"?Sf(t.agent,e,t.name):null}async function Il(t){if(!t)return null;let e=await Xe(t.name).catch(()=>"");return Kn(t,e)}function Hh(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 Wh(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 $r(t){return typeof t=="string"?t.trim():""}var qh=/^\s*─{8,}\s*$/,PA=/^❯[  ]?(.*)$/;function Ko(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(qh.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(qh.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(PA);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 ne(t){return String(t||"").replace(/\s+/g," ").trim()}function Kh(t,e,{slackMs:n=2e4}={}){let r=ne(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&&ne(o.text).includes(r))}function Gh(t,e,n,{busy:r=!1}={}){let o=Ko(t,n);return o&&Rl(e,o)?"stuck":r||o?"submitted":"unknown"}function Rl(t,e){let n=ne(t),r=ne(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import BA from"node:crypto";var $l=new Set(["cursor","codex"]),Vh=4,DA=4,jA=2,LA=24,zh=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Go=t=>typeof t=="string"?t.trim():"";function FA(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,Vh)){let o=Go(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,DA)){let c=Go(i?.label);if(!c)return null;let l=Go(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<jA)return null;n.push({question:o,header:Go(r?.header).slice(0,LA),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Ol(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;zh.lastIndex=0;let e=null;for(let o;o=zh.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=FA(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function UA(t,e,n){let r=BA.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Ml(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=Vh}={}){if(!Array.isArray(t)||!e||!$l.has(n))return null;let s=null;for(let l=t.length-1;l>=0;l--){let u=t[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==e||s.agent!==n)return null;let a=s.ask?.questions||Ol(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=UA(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 Jh(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=Go(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 ST(t,e){let n=await we(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 vT(t,e,n){if(!(await we(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 kr(t){return((await we(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var Rm=255;async function Uc(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>Rm,a=s?`${o.slice(0,Rm-1).trimEnd()}\u2026`:o,i=await we(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 Hc(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await we(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 Bm={linear:{api:Wc,config:Pn},jira:{api:Lc,config:wa}};async function Xc(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(Bm)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function jn(t){let e=Bm[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function Yc(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 Dm=()=>process.env.BOSTRAT_STATE_DIR||Nm.join(process.env.HOME||TT.tmpdir(),".bostrat"),Vc=()=>Nm.join(Dm(),"tickets.json"),jm=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),ya=t=>`${jm()}/track/${t}`,rn=null,qc=null,Kc=Promise.resolve();async function No(){return rn||(qc??=Co.readFile(Vc(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(rn=t,!e&&ft()){let{records:n,source:r}=await vt("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;rn[s]=i}Object.keys(rn).length&&await zc().catch(()=>{})}}return rn}).finally(()=>qc=null),qc)}async function zc(){let t=JSON.stringify({tickets:rn},null,2),e=`${Vc()}.${process.pid}.tmp`;await Co.mkdir(Dm(),{recursive:!0}),await Co.writeFile(e,t),await Co.rename(e,Vc())}async function on(t=null){if(Kc=Kc.then(zc,zc),await Kc.catch(()=>{}),!t)return;let e=rn?.[t];e?Rt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):Qt("tickets",t).catch(()=>{})}async function ge(t){return(await No())[t]??null}async function ba(){return{...await No()}}async function Lm(){return(await Xc()).length>0}async function xr(t,e,{source:n="api"}={}){let r=await Xc();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 No())[t.id]=c,await on(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 Mo(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 Oo(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Hm(t.id,c,"linked"),c}async function Zc(t){let e=await No();return e[t]?(delete e[t],await on(t),!0):!1}var Jc=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Fm(t,e){if(!t.track?.cwd)return null;let n=ue(await D({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function AT(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Jc(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${Jc(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function ET(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(Jc(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Mo(t,e){let n=await jn(e);if(!n)return;let r=await Fm(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:ya(t),title:`Bostrat Track: ${e.track.name}`,subtitle:ET(e,r)})}function ka(t){let e=new URL(jm());return`${e.protocol}//${t}.${e.host}/`}var _T={linked:"linked",opened:"prOpened",merged:"prMerged"};function IT(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 Um(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await on(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 Hm(t,e,n){let r=await jn(e);if(!r)return;let o=r.cfg.ticketFlow?.[_T[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=Yc(s,a),c=IT(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 Um(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:Pm(),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 Wm(t){let e=await ge(t);if(!e)throw new Error("no ticket linked to this Track");let n=await jn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=Yc(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await on(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 Qc(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await ge(t);if(!r)throw new Error("no ticket linked to this Track");let o=await jn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Yc(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 on(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Um(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function qm(t,e){let n=await ge(t);if(!n)throw new Error("no ticket linked to this Track");let r=await jn(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 Km(t,e,n){if(!e)throw new Error("offerId required");let r=await D({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 Qc(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 $m=50;async function Gm(){let t=await Pn();if(!t)throw new Error("no ticketing integration configured");return kr(t)}function RT(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>$m)throw new Error(`too many issues (${n.length}); cap is ${$m} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function $T(t,e,n,r){let o=r??await kr(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 ge(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 Vm(t,e){let n=await Pn();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=RT(e),o=await kr(n),s=await $T(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Pm();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 zm(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await D({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 Pn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await kr(a)).find(g=>g.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let m=await Uc(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 g=await Hc(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:g.key,title:g.title,url:g.url})}catch(g){u.push({title:m.title,message:g?.message||String(g)})}await R({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await ge(t.id)&&(p=await xr(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function Oo(t,e){let n=await jn(t);n&&await n.api.createComment(n.cfg,t,e)}async function OT(t,e,n){let r=await jn(t);if(!r)return null;let o=await en(e);if(!o)return null;let s=_e(o,n);if(!s)return null;let a=await Co.readFile(Ht(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 Om=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Gc=new Map;function CT(t,e){if(Om()<=0)return Mo(t,e);if(Gc.has(t))return;let n=setTimeout(()=>{Gc.delete(t),ge(t).then(r=>r?Mo(t,r):null).catch(()=>{})},Om());n.unref?.(),Gc.set(t,n)}var Cm=new Map;async function Mm(t,e,n){let r=Cm.get(t)??0;Date.now()-r<600*1e3||(Cm.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 MT(t,{resolveTrack:e=null}={}){let n=await ge(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await Xc())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 xr(s,o,{source:"chat"})}catch(a){await Mm(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 on(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await on(t.trackId),await Mo(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 on(t.trackId),await Mo(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Fm(n,t.trackId).catch(()=>null):null;await Oo(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: ${AT(o)}.`:"")),await Hm(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await OT(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Oo(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 Oo(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 Oo(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 CT(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Mm(t.trackId,s?.cwd??null,o)}}function Jm({resolveTrack:t}){Jt.on("event",e=>{MT(e,{resolveTrack:t}).catch(()=>{})})}async function Xm(){if(!ft())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await vt("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await No(),r=0;for(let[o,s]of Object.entries(n))await Rt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import NT from"node:crypto";import xa from"node:fs/promises";import Ym from"node:path";import PT from"node:os";var Zm=process.env.BOSTRAT_STATE_DIR||Ym.join(process.env.HOME||PT.tmpdir(),".bostrat"),nl=Ym.join(Zm,"pr-links.json"),U=(t,e)=>`${t}#${Number(e)}`,Qm=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=NT.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},sn=null,tl=null,el=Promise.resolve();async function gt(){return sn||(tl??=xa.readFile(nl,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(sn=t,!e&&ft()){let{records:n,source:r}=await vt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;sn[U(i.repo,i.number)]=i}Object.keys(sn).length&&await rl().catch(()=>{})}}return sn}).finally(()=>tl=null),tl)}async function rl(){let t=JSON.stringify({links:sn},null,2),e=`${nl}.${process.pid}.tmp`;await xa.mkdir(Zm,{recursive:!0}),await xa.writeFile(e,t),await xa.rename(e,nl)}async function St(t){el=el.then(rl,rl),await el.catch(()=>{});let e=sn?.[t];e&&Rt("prlinks",{id:Qm(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var BT=new Set(["api","observed","reconciled"]);async function Wt({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 gt(),c=U(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:BT.has(a),firstSeen:Date.now()},await St(U(t,e)),{link:i[c],created:!0})}async function Po(t,e){if(!t||e==null)return null;let n=(await gt())[U(t,e)];return!n||n.announced?null:(n.announced=!0,await St(U(t,e)),n)}async function $t(t,e){return!t||e==null?null:(await gt())[U(t,e)]??null}async function an(t,e,n){if(!t||e==null)return null;let r=(await gt())[U(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 St(U(t,e)),r):null}async function th(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await gt())[U(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await St(U(t,e)),o):null}async function eh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await gt())[U(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await St(U(t,e)),o):null}async function Sa(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await gt())[U(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await St(U(t,e)),r)}async function va(t,e){if(!t||e==null)return null;let n=(await gt())[U(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await St(U(t,e)),n)}async function Ta(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await gt())[U(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await St(U(t,e)),r)}async function Aa(t,e){if(!t||e==null)return null;let n=(await gt())[U(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await St(U(t,e)),n)}async function nh(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await gt())[U(t,e)];return r?(r.baseFoldAt=n,await St(U(t,e)),r):null}async function rh(t,e){if(!t||e==null)return null;let n=(await gt())[U(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await St(U(t,e)),n)}async function oh(t,e,n){if(!t||e==null||!n)return null;let r=(await gt())[U(t,e)];return r?(r.lastHeadSha=n,await St(U(t,e)),r):null}async function Bo(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await gt())[U(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await St(U(t,e)),r):null}async function Sr(t,e,n=!0){if(!t||e==null)return null;let r=(await gt())[U(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await St(U(t,e)),r):null}async function Ea(t,e,n=!0){if(!t||e==null)return null;let r=(await gt())[U(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await St(U(t,e)),r):null}async function sh(t,e,n){if(!t||e==null)return null;let r=(await gt())[U(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 St(U(t,e)),r}async function Do(t,e){if(!t||e==null)return null;let n=(await gt())[U(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 St(U(t,e)),n)}var DT=new Set(["merged","closed","lost"]);async function ah(){return Object.values(await gt()).filter(t=>!DT.has(t.state))}async function ye(){return Object.values(await gt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function ih(){if(!ft())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await vt("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await gt(),r=0;for(let[o,s]of Object.entries(n))await Rt("prlinks",{id:Qm(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var lh=t=>Math.round(t*100)/100;function jT(t){return!t||t.kind==="setup"?[]:Dt(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():ch.basename(e.base)}))}function uh(t){let e=new Map;for(let n of t||[])for(let r of jT(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function _a(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 ol=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function LT(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 sl(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(ol(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(ol(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(`${ol(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=ue(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?LT(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function FT(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 dh(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=_a(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:lh(l),firstActivityAt:c,lastActivityAt:i}}async function fh(t){let e=uh(t),n=await ye().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 D(i).catch(()=>[]);r.set(i.id,ue(c).totalUsd)}let s=[...e.values()].map(a=>dh(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function ph(t,e){let r=uh(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([ye().catch(()=>[]),Cn().catch(()=>[]),ua().catch(()=>[]),pa().catch(()=>[]),ba().catch(()=>({}))]),l=new Map,u=[],f=(d,p)=>(_a(p)||p.createdAt||0)-(_a(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let p=await D(d).catch(()=>[]),m=ue(p);l.set(d.id,m.totalUsd);let g=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:FT(d),handoffs:(d.events||[]).filter(y=>y.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:Dt(d.workspace).map(y=>y.repo||ch.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:g?{provider:g.provider,key:g.key,url:g.url??null}:null,usd:lh(m.totalUsd),lastActivityAt:_a(d)})}return{...dh(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as UT}from"node:child_process";import{promisify as HT}from"node:util";import{randomUUID as WT}from"node:crypto";var Ln=HT(UT),Ia=t=>new Promise(e=>setTimeout(e,t)),qT=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,mh=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,KT=250,GT=400,hh={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 gh(t){let e=await kf(t);if(!e)return!1;try{return await Ln("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function al(t,e){let n=Date.now()+e;for(;;){if(await gh(t))return!0;if(Date.now()>n)return!1;await Ia(KT)}}async function VT(t,e){let n=hh[e]||hh.default;n.escape&&(await bt(t,["Escape"],{kind:"swap-exit"}),await Ia(150)),n.wipe&&await Us(t,400,{kind:"swap-exit"}),await Hs(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Ee(e).submitDelayMs||0;await Ia(Math.max(r,250)),await bt(t,["Enter"],{kind:"swap-exit"})}async function zT(t,e){try{let{stdout:n}=await Ln(N,P("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function wh(t){await Ln(N,P("respawn-pane","-k","-t",t))}async function il(t,e={}){if(!wt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await Z(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||cr(n),a=Number(e.exitTimeoutMs)||qT;await V({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await gh(t)&&(await VT(t,o),!await al(t,a)&&(i=!0,await V({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await wh(t),!await al(t,mh))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&ir(n),l=c?_s():null,u=await zT(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Ln(N,P("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Ln(N,P("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?js(y,l):y}if(f&&!i&&(await wh(t),!await al(t,mh)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||WT(),m=d?n:tc(n,p),g=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Ke(r.cwd),s==="codex"&&await er(r.cwd),s==="cursor"&&await nr(r.cwd),await Rn(t),await Ln(N,P("set-option","-t",t,"@bostrat-sid",p)),await Ln(N,P("set-option","-t",t,"@bostrat-agent",s)),await Ia(GT),await xt(t,m,{kind:"swap-launch"}),await V({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:g,forced:i,envRespawned:f}}import _A from"node:fs/promises";function JT(t){let e=[],n="",r=null;for(let o=0;o<t.length;o++){let s=t[o];if(r){n+=s,s===r&&t[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&t[o+1]==="&"){e.push(n),n="",o++;continue}if(s==="|"&&t[o+1]==="|"){e.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
91
+ `){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function jo(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 Lo(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 Tr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Et=t=>t&&!/[$`]/.test(t)?t:null,pt=t=>t.replace(/\/+$/,"").split("/").pop()||t,vr=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},kh=t=>t.startsWith("-"),yh=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function st(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(kh(o)){e.has(o)&&r++;continue}n.push(o)}return n}var XT={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 ${pt(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"},YT={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 xh(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 ZT(t){let e=xh(t),n=Et(st(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${pt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${pt(n)}`:t.includes("-i")&&n?`Edit ${pt(n)}`:null}function cl(t){if(t.includes("--files")){let s=Et(st(t,yh)[0]);return s&&s!=="."?`List files in ${vr(s)}`:"List files"}let e=st(t,yh),n=e[0];if(!n)return null;let r=Et(e.slice(1).find(s=>s!==".")),o=Tr(n,40);return r?`Search for \u201C${o}\u201D in ${vr(r)}`:`Search for \u201C${o}\u201D`}function ll(t){let e=st(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 ul(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=st(t)[0];return n?`Run ${pt(n)}`:null}function QT(t){let e=st(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 bh(t){return t.find(e=>/^https?:\/\//.test(e))||null}var tA={git:t=>{let e=st(t,new Set(["-C","-c"])),n=XT[e[0]];return n?n(e,t):null},gh:t=>{let e=st(t),n=YT[e[0]];return n?n(e):null},sed:ZT,rg:cl,grep:cl,egrep:cl,nl:t=>{let e=Et(st(t)[0]);return e?`Read ${pt(e)}`:null},cat:t=>{let e=Et(st(t)[0]);return e?`Read ${pt(e)}`:null},head:t=>{let e=Et(st(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${pt(e)}`:null},tail:t=>{let e=Et(st(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${pt(e)}`:null},wc:t=>{let e=Et(st(t)[0]);return e?`Count lines in ${pt(e)}`:"Count input lines"},ls:t=>{let e=Et(st(t)[0]);return e&&e!=="."?`List files in ${vr(e)}`:"List files"},find:t=>Et(t[1])&&!kh(t[1])&&t[1]!=="."?`Find files in ${vr(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Et(st(t)[0]);return e?`Create directory ${vr(e)}`:null},touch:t=>{let e=Et(st(t)[0]);return e?`Create ${pt(e)}`:null},rm:t=>{let e=Et(st(t)[0]);return e?`Delete ${vr(e)}`:null},mv:t=>{let e=Et(st(t)[0]);return e?`Move ${pt(e)}`:null},cp:t=>{let e=Et(st(t)[0]);return e?`Copy ${pt(e)}`:null},chmod:t=>{let e=Et(st(t)[1]);return e?`Change permissions on ${pt(e)}`:null},diff:t=>{let e=st(t);return e.length>=2?`Compare ${pt(e[0])} and ${pt(e[1])}`:null},curl:t=>{let e=bh(t);return e?`Fetch ${Tr(e,56)}`:"Fetch a URL"},wget:t=>{let e=bh(t);return e?`Fetch ${Tr(e,56)}`:"Fetch a URL"},npm:ll,pnpm:ll,yarn:ll,npx:t=>{let e=st(t)[0];return e?`Run ${e}`:null},node:t=>ul(t,"Node"),python:t=>ul(t,"Python"),python3:t=>ul(t,"Python"),make:t=>{let e=st(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:QT,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=st(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=st(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=st(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},eA=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function nA(t){let e=Lo(jo(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(pt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return dl(e[2]);let n=pt(e[0]);if(eA.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=tA[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function dl(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=JT(e);for(let i=0;i<r.length-1;i++){let c=Lo(jo(r[i])),l=Lo(jo(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(pt(c[0]))&&pt(l[0])==="sed"){let u=xh(l),f=st(c)[0];if(u&&f)return Tr(u.from===u.to?`Read line ${u.from} of ${pt(f)}`:`Read lines ${u.from}\u2013${u.to} of ${pt(f)}`,88)}}let o=[];for(let i of r){let c=nA(i);c&&o.push({d:c,head:pt(Lo(jo(i))[0]||""),sub:st(Lo(jo(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":Tr(s?s.d:o[0].d,88)}var pl=new Set(["Bash"]);function Ar(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 cn(t,e=""){let n=e.trim();return n||dl(t)||Ar(t)}function fl(t,e){if(t){if(Array.isArray(t)){for(let n of t)fl(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&pl.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))fl(n,e)}}}async function Sh(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Nt(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(fl(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:cn(f.command,f.description),ts:u})}return o.slice(-n)}function vh(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,g)=>g>=s&&(m.ts||0)>(l.ts||0)),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 rA=/^Async (?:agent|task) launched/i,oA=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 Th(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Nt(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?Ar(p):null),detail:p?Ar(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&rA.test(oA(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 Fo from"node:fs";import aA from"node:fs/promises";import Fn from"node:path";import iA from"node:os";import cA from"node:crypto";import lA from"node:zlib";var Ra=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),ne=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Er=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function ln(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 ml=t=>`"${String(t).replace(/"/g,'\\"')}"`;function sA(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 Ah(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:ne(`read ${ln(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Er(r)}`:`Read ${Er(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?ln(e.path,n):null,s=e?.glob?` --glob ${ml(e.glob)}`:"",a=ne(String(r),40);return{command:ne(`grep ${ml(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?ln(e.target_directory,n):null;return{command:ne(`glob ${ml(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${ne(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:ne(`edit ${ln(e.path,n)}`,140),description:`Edit ${Er(e.path)}`}:null;case"Write":return e?.path?{command:ne(`write ${ln(e.path,n)}`,140),description:`Write ${Er(e.path)}`}:null;case"Delete":return e?.path?{command:ne(`delete ${ln(e.path,n)}`,140),description:`Delete ${Er(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>ln(s,n)).filter(Boolean);return{command:ne(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=sA(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:ne(`apply_patch ${ln(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Er(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:ne(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var uA=process.env.BOSTRAT_CURSOR_CHATS||Fn.join(iA.homedir(),".cursor","chats");function Ih(t){let e=cA.createHash("md5").update(String(t)).digest("hex");return Fn.join(uA,e)}function Rh(t){try{let e=JSON.parse(Fo.readFileSync(Fn.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 dA(t){let e=Ih(t),n;try{n=Fo.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Fn.join(e,o.name),a=Fn.join(s,"store.db");if(!Fo.existsSync(a))continue;let i=Rh(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 $a(t,e){if(!t)return null;if(e){let n=Fn.join(Ih(t),e),r=Fn.join(n,"store.db");if(Fo.existsSync(r))return{id:e,dir:n,store:r,...Rh(n)||{createdAtMs:null,updatedAtMs:null}}}return dA(t)}function Eh(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function fA(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=lA.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function pA(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 mA(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!Ra.has(r.toolName))continue;let o=Ah(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 hA=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():"",wA=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],_h=process.getBuiltinModule?.("node:sqlite"),_r={turns:[],trailingCommands:[],model:null,requestIds:[]};async function hl(t,e){if(!_h)return _r;let{DatabaseSync:n}=_h,r;try{r=new n(t,{readOnly:!0})}catch{return _r}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 _r;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return _r}let a=s.lastUsedModel||null,i=s.latestRootBlobId?Eh(r,s.latestRootBlobId):null;if(!i)return{..._r,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of pA(i)){let m=fA(Eh(r,p));if(!m)continue;let g=m.providerOptions?.cursor;if(g?.requestId&&d.push(g.requestId),g?.modelProviderMessageId&&d.push(g.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(...mA(m.content,e).map((S,E)=>({...S,id:S.id||`${p}:${E}`}))),u.push(...wA(m.content));let y=hA(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 _r}finally{try{r.close()}catch{}}}function $h(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(Fo.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 Oh(t,e,{limit:n=80}={}){let r=$a(t,e);if(!r)return[];let{turns:o,model:s}=await hl(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=$h(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 Ch(t,e){let n=$a(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await hl(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 Mh(t,e,{limit:n=80}={}){let r=$a(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await hl(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=$h(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:cn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:cn(u.command,u.description),ts:i+1});return l}async function Nh(t,e){let n=$a(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await aA.stat(n.store)).mtimeMs)}catch{return null}}import Ph from"node:fs";import gl from"node:fs/promises";import wl from"node:path";import gA from"node:os";var yA=process.env.BOSTRAT_CODEX_SESSIONS||wl.join(process.env.CODEX_HOME||wl.join(gA.homedir(),".codex"),"sessions");async function Bh(t){try{return await gl.realpath(t)}catch{return t}}function bA(t=yA){let e=[],n=r=>{let o;try{o=Ph.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=wl.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:Ph.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function kA(t,e=1048576){let n;try{n=await gl.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 xA(t){let e=await kA(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 Un(t,e){if(!t&&!e)return null;let n=bA();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await Bh(t);for(let o of n){let s=await xA(o.file);if(s&&(s===t||await Bh(s)===r))return o}return null}var SA=new Set(["output_text","text"]),vA=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&SA.has(e.type)).map(e=>e.text).join(`
98
+ `).trim():"";async function Dh(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Un(t,e);if(!o)return[];let s;try{s=await Nt(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:bl,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 g=m.payload;if(!g)continue;if((g.type==="function_call"||g.type==="custom_tool_call")&&g.name){u=!0,yl.has(g.name)||c.push(g.name);continue}if(g.type!=="message"||g.role!=="assistant")continue;u=!0;let y=vA(g.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 Uo=new Set(["exec_command","shell","exec"]),yl=new Set(["wait","write_stdin"]),bl="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 TA(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 AA(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 EA(t){let e=String(t||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(e);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=e.slice(s,a+1<n.length?n[a+1]:e.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=AA(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function jh(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Un(t,e);if(!o)return[];let s;try{s=await Nt(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"&&Uo.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=TA(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=EA(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,m)=>{a.push({id:d?m?`${d}:${m}`:d:null,tool:u.name,command:p,description:"",summary:cn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Lh(t,e){let n=await Un(t,e);if(!n)return null;try{return Math.round((await gl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var Ir=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}},kl=class extends Ir{transcript(e){return!e?.cwd||!e?.sessionId?null:Ae(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?lc(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?Sh(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?Th(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await _A.stat(n)).mtimeMs)}catch{return null}}},xl=class extends Ir{async readMessages(e,n){return Oh(e.cwd,e.sessionId,n)}async readCommands(e,n){return Mh(e.cwd,e.sessionId,n)}async updatedAt(e){return Nh(e.cwd,e.sessionId)}},Sl=class extends Ir{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return Dh(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return jh(e.cwd,this.sessionId(e),n)}async updatedAt(e){return Lh(e.cwd,this.sessionId(e))}},IA=new Ir("unknown"),Fh=new Map;function vl(t){return Fh.set(t.agent,t),t}function Ho(t){return Fh.get(t)||IA}vl(new kl("claude",pl));vl(new xl("cursor",Ra));vl(new Sl("codex",Uo));var Tl=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function Al(t,e={}){return(await Ho(t.agent).readMessages(t,e)).map(Tl(t))}async function Uh(t,e={}){return(await Ho(t.agent).readCommands(t,e)).map(Tl(t))}async function Hh(t,e={}){return(await Ho(t.agent).readJobs(t,e)).map(Tl(t))}var Kh=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Il=/Chat about this/i,Hn=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Wh=/^(type something|chat about this|submit)\b/i,Wn=/^[─—-]{6,}\s*$/,Rl=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function qn(t){let e=t.search(Rl);return(e>=0?t.slice(0,e):t).trim()}function RA(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function $A(t){let e=String(t).trim();return!!(!e||Wn.test(t)||Rl.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function OA(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=CA(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function CA(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 qh(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Gh=/^\s*Switch model\?\s*$/m;function MA(t,e=""){let n=String(t);if(!Gh.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(Hn);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&&!Wn.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:qn(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 Vh=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,NA=/^\s*[─—▔-]{6,}\s*$/;function PA(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||!Vh.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(Hn);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&&!(NA.test(n[c])||Wn.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:qn(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 BA(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(Hn);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 El=/Resume from summary/i,DA=/^\s*This session is\b/;function jA(t,e=""){let n=String(t);if(!El.test(n))return null;let r=n.replace(/\r/g,"").split(`
105
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if(DA.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(Hn);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&&!Wn.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>El.test(u.label)))return null;let i=s.map(u=>({label:qn(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 Kn=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function $l(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(Hn);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 zh=/Choose the text style that looks best with your terminal/,Jh=/Let's get started\./;function LA(t,e=""){let n=String(t);if(!zh.test(n)||!Jh.test(n)||Kn.test(n))return null;let r=n.replace(/\r/g,"").split(`
106
+ `),o=$l(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:qn(s.label),description:""}))}]}:null}var Xh=/Select login method:/;function FA(t,e=""){let n=String(t);if(!Xh.test(n)||Kn.test(n))return null;let r=n.replace(/\r/g,"").split(`
107
+ `),o=$l(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:qn(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 Ol=/Paste code here if prompted/,UA=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function HA(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!UA.test(o)||Ol.test(t[r]))break;n+=o}return n}function WA(t,e=""){let n=String(t);if(!Ol.test(n)||Kn.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=HA(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 _l=/OAuth error:/,qA=/Press Enter to retry\./;function KA(t,e=""){let n=String(t);if(!_l.test(n)||!qA.test(n)||Kn.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
109
+ `)].reverse().find(a=>_l.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 Yh=/Quick safety check: Is this a project you created or one you trust/;function GA(t,e=""){let n=String(t);if(!Yh.test(n)||Kn.test(n))return null;let r=n.replace(/\r/g,"").split(`
110
+ `),o=$l(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:qn(s.label),description:""}))}]}:null}var Zh=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function VA(t,e=""){let n=String(t);if(Kn.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(Zh);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(Wn.test(r[l])||Rl.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 zA(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Il.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 JA(t,e=""){let n=String(t);if(!Kh.test(n)||!Il.test(n))return null;let r=n.replace(/\r/g,"").split(`
112
+ `),o=zA(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 C=r[v].match(Hn);C&&(u<0&&(u=v),l.push({line:v,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 v of l)for(let C=v.line+1;C<Math.min(r.length,v.line+4)&&!(Hn.test(r[C])||Wn.test(r[C]));C++){let H=r[C].trim();if(!(!H||$A(r[C])||Wh.test(H))){v.desc=RA(H);break}}let f=l.filter(v=>!Wh.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,g=[];for(let v=m;v<u;v++){let C=r[v].trim();C&&!Wn.test(r[v])&&!/[☐☒]/.test(r[v])&&g.push(C)}let y=g.join(" ")||(s>=0?qh(r[s]):"")||"The agent is asking a question",b=s>=0?OA(r[s],y):null,S=b?.currentTabLabel||(s>=0?qh(r[s]):""),E=f.map(v=>({label:qn(v.label),description:v.desc})),M=E.map(v=>v.label).join("|"),$=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${S}:${y}:${M}`.slice(0,240),j=b?`claude:ask:${b.tabs.map(v=>v.label).join("|")}`.slice(0,240):$;return{provider:"claude",box:e,multiSelect:d,cursorIndex:p,promptId:$,groupId:j,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:y,header:S,multiSelect:d,options:E}]}}function Rr(t,e){let n=String(e||"");return n?t==="claude"?Kh.test(n)&&Il.test(n)||Gh.test(n)||El.test(n)||Vh.test(n.trimEnd().split(`
113
+ `).pop()||"")?!0:Kn.test(n)?!1:zh.test(n)&&Jh.test(n)||Xh.test(n)||Ol.test(n)||_l.test(n)||Yh.test(n)||Zh.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 Gn(t,e){return!t||!e?null:t.agent==="claude"?BA(e,t.name)||JA(e,t.name)||MA(e,t.name)||jA(e,t.name)||KA(e,t.name)||WA(e,t.name)||FA(e,t.name)||LA(e,t.name)||GA(e,t.name)||VA(e,t.name)||PA(e,t.name):t.agent==="cursor"||t.agent==="codex"?Ff(t.agent,e,t.name):null}async function Cl(t){if(!t)return null;let e=await Xe(t.name).catch(()=>"");return Gn(t,e)}function Qh(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 tw(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 $r(t){return typeof t=="string"?t.trim():""}var ew=/^\s*─{8,}\s*$/,XA=/^❯[  ]?(.*)$/;function Wo(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(ew.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(ew.test(n[c])){o=c;break}if(o<0)return null;let s=n.slice(o+1,r);if(!s.length)return null;let a=s[0].match(XA);if(!a)return null;let i=[a[1].trim(),...s.slice(1).map(c=>c.trim())].filter(Boolean);return i.length?i.join(" "):null}function qt(t){return String(t||"").replace(/\s+/g," ").trim()}function nw(t,e,{slackMs:n=2e4}={}){let r=qt(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&&qt(o.text).includes(r))}function rw(t,e,n,{busy:r=!1}={}){let o=Wo(t,n);return o&&Ml(e,o)?"stuck":r||o?"submitted":"unknown"}function Ml(t,e){let n=qt(t),r=qt(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import YA from"node:crypto";var Nl=new Set(["cursor","codex"]),sw=4,ZA=4,QA=2,tE=24,ow=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,qo=t=>typeof t=="string"?t.trim():"";function eE(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,sw)){let o=qo(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,ZA)){let c=qo(i?.label);if(!c)return null;let l=qo(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<QA)return null;n.push({question:o,header:qo(r?.header).slice(0,tE),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Pl(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;ow.lastIndex=0;let e=null;for(let o;o=ow.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=eE(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function nE(t,e,n){let r=YA.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Bl(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=sw}={}){if(!Array.isArray(t)||!e||!Nl.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||Pl(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=nE(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 aw(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=qo(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 Xh=`
118
+ ${o}`),s||""}var iw=`
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,40 +135,40 @@ 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 HA}from"node:child_process";import{promisify as WA}from"node:util";var Nl=WA(HA),Yh=64*1024*1024;async function ht(t,e,{input:n}={}){let r=Nl("git",["-C",t,...e],{maxBuffer:Yh});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Ca(t){if(!t)return!1;try{let{stdout:e}=await ht(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Zh(t){let{stdout:e}=await ht(t,["rev-parse","--show-toplevel"]);return e.trim()}async function qA(t){try{return await ht(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function zo(t,e="bostrat: subtask snapshot"){let{stdout:n}=await ht(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Nl("git",["-C",t,...a],{maxBuffer:Yh,env:o,...i});try{let a=await qA(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 Nl("rm",["-f",r])}catch{}}}async function Pl(t,e){let{stdout:n}=await ht(t,["status","--porcelain"]);return n.trim()?(await ht(t,["add","-A"]),await ht(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function Bl(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await ht(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 Dl(t,e){if(!e)return 0;try{let{stdout:n}=await ht(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Or(t,e,n,r){return await ht(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Qh(t,e){try{return await ht(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function tw(t,e,n){return await ht(t,["worktree","add",e,n]),e}async function Na(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await ht(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 ht(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function ew(t){await ht(t,["init","-q"]),await ht(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Vt(t,e){try{await ht(t,["worktree","remove","--force",e])}catch{try{await ht(t,["worktree","prune"])}catch{}}}function KA(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 jl(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Ll(t,e,n,r){let{stdout:o}=await ht(t,jl(["diff","--shortstat",e,n],r));return KA(o)}async function Fl(t,e,n,r){let{stdout:o}=await ht(t,jl(["diff","--name-only",e,n],r));return o.split(`
140
- `).map(s=>s.trim()).filter(Boolean)}async function Ul(t,e,n,r){let{stdout:o}=await ht(t,jl(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Cl(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Cl(t,o,["--3way","--whitespace=nowarn"]),a=await GA(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Cl(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Cl(t,e,n){try{return await ht(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function GA(t){try{let{stdout:e}=await ht(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
141
- `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import zA from"node:fs/promises";import VA from"node:os";import nw from"node:path";var Pa=t=>new Promise(e=>setTimeout(e,t)),JA=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,XA=300,aw=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 4.8"},{alias:"sonnet",label:"Sonnet 4.6"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],YA=new Set([...aw.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),ZA=[{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.4",label:"GPT-5.4"},{alias:"gpt-5.4-mini",label:"GPT-5.4 Mini"},{alias:"gpt-5.3-codex",label:"Codex 5.3"},{alias:"gpt-5.2",label:"GPT-5.2"}],iw=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),cw=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-4-8",label:"Opus 4.8"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}];function lw(t){return t==="codex"?ZA:t==="cursor"?cw:aw}function rw(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 ow(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 QA(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 sw(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 uw(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&&!iw.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=cw.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!YA.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Vo(t,e,{timeoutMs:n=JA}={}){let r=Date.now()+n;for(;;){let o=await gt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await Pa(XA)}}async function tE(t,e){return await vt(t,`/model ${e.model}`,_e("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function eE(t,e){await Ls(t),await vt(t,"/model",{enter:!1}),await Pa(250),await xt(t,["Enter"],{kind:"model-switch"});let n=await Vo(t,rw);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await xt(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=QA(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await vt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await Vo(t,i=>ow(i)||!rw(i)&&o(i)&&{done:o(i)});if(!s)throw await xt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?iw.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 xt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await vt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Vo(t,i=>!ow(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 nE(){let t=nw.join(process.env.CURSOR_CONFIG_DIR||nw.join(VA.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await zA.readFile(t,"utf8"))?.model||null}catch{return null}}async function rE(t,e){await Ls(t),await Us(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await Vo(t,sw),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 Vo(t,sw,{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 xt(t,["Down"],{kind:"model-switch"}),await Pa(250)}if(!n||!s||!o(s)){await xt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await xt(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 xt(t,["Enter"],{kind:"model-switch"}),await Pa(800);let a=await nE();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 dw(t,e,n){return t==="codex"?eE(e,n):t==="cursor"?rE(e,n):tE(e,n)}var Wt=t=>t||null;function Da(t){let e=t||{},n=oE(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function oE(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Wt(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:Wt(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:Wt(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:Wt(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:Wt(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:Wt(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:Wt(t.agent),payload:{...Mr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Wt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Mr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Wt(t.agent),payload:{change:"subtask-resumed",...Mr(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",...Mr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Mr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Wt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Mr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Wt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var Ba=Promise.resolve(),Wl=t=>(Ba=Ba.then(t).catch(()=>{}),Ba),pw=()=>Ba;function mw(t,e){let n=Da(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Wl(()=>$(r,n))}var fw=new Set;async function hw(t){if(!(!t?.id||fw.has(t.id))){fw.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=Da(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(t,o)}}catch{}}}function Mr(t){let{type:e,at:n,...r}=t;return r}var Hl=new Map;function ww(t,e,n){!n?.promptId||Hl.get(t.id)===n.promptId||(Hl.set(t.id,n.promptId),Wl(()=>$(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Wt(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 ql(t,e,n,r={}){Hl.delete(t.id),Wl(()=>$(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Wt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as BE}from"node:crypto";var yw=/(?<!\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,bw=/(?<!\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,kw=/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,xw=/(?<!\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,sE=new RegExp(`\\b(?:${yw}|${bw}|${kw}|${xw})\\b`,"i"),aE=/\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,iE=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,cE=90;function lE(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function Sw(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of lE(n).reverse()){if(!sE.test(r))continue;let o=r.endsWith("?")&&r.length<=cE&&iE.test(r);if(aE.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var vw=["commit","rebase","push","pr"],uE=new RegExp(`\\b(?:${yw})\\b`,"i"),dE=new RegExp(`\\b(?:${xw})\\b`,"i"),fE=new RegExp(`\\b(?:${bw})\\b`,"i"),pE=new RegExp(`\\b(?:${kw})\\b`,"i"),mE=/\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 Tw(t){let e=String(t||""),n=[];uE.test(e)&&n.push("commit"),dE.test(e)&&n.push("rebase"),fE.test(e)&&n.push("push");let r=mE.test(e);!r&&pE.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 ja(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(m=>`${m.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 m=a.has("commit")?"the branch":`this branch${e?` (${e})`:""}`;u.push(i?`rebase ${m} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${m} 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 m=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",h=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${h}${m}`:`push this branch${e?` (${e})`:""} from each changed checkout (${c}) to its remote${m}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${h}${m}`:`push this branch${l} to the remote${m}`)}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(/^./,m=>m.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 hE=3e4;function Aw(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<=hE&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Ew(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 gw={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function _w(t){let e=(Array.isArray(t)?t:[]).filter(r=>gw[r]).map(r=>gw[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 kE}from"node:child_process";import{promisify as xE}from"node:util";import wE from"node:fs";import Kl from"node:fs/promises";import Ow from"node:path";import gE from"node:os";var Mw=process.env.BOSTRAT_STATE_DIR||Ow.join(process.env.HOME||gE.tmpdir(),".bostrat"),La=Ow.join(Mw,"github-metrics.json"),Cw=60*6e4,Iw=5*6e4,yE=6e4,Cr=Date.now(),je=new Map,ln=null,Gn=0,Rw=0,Fa=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(wE.readFileSync(La,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Cw?(Cr=t.windowStart,je=new Map(Object.entries(t.byCaller||{})),ln=t.lastHour||null):Fa(new Map(Object.entries(t.byCaller||{})))&&(ln={start:t.windowStart,total:Fa(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var $w=Promise.resolve();function Nw(t,e=!1){if(!e&&t-Rw<yE)return;Rw=t;let n=JSON.stringify({windowStart:Cr,byCaller:Object.fromEntries(je),lastHour:ln});$w=$w.then(()=>Kl.mkdir(Mw,{recursive:!0})).then(()=>Kl.writeFile(`${La}.tmp`,n)).then(()=>Kl.rename(`${La}.tmp`,La)).catch(()=>{})}function Pw(t){t-Cr<Cw||(ln={start:Cr,total:Fa(je),byCaller:Object.fromEntries(je)},ln.total&&console.log(`[github] ${ln.total} calls last hour`,ln.byCaller),Cr=t,je=new Map,Nw(t,!0))}function Le(t="other",e=Date.now()){Pw(e),je.set(t,(je.get(t)||0)+1),Nw(e)}function Bw(t=Date.now()){return Pw(t),{windowStart:Cr,total:Fa(je),byCaller:Object.fromEntries(je),lastHour:ln,backoffUntil:Gn>t?Gn:null}}var bE=/rate limit|too quickly|abuse detection|HTTP 429/i;function un(t){return bE.test(String(t?.message||t||""))}function dn(t,e=Date.now()){return Gn<=e&&console.warn(`[github] rate limited \u2014 backing off ${Iw/6e4} min:`,String(t?.message||t||"").slice(0,200)),Gn=Math.max(Gn,e+Iw),Gn}function Ua(t=Date.now()){return Math.max(0,Gn-t)}async function Gl(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await gt(t,{lines:At}).catch(()=>"");if(!Ze(e,r))return Ef(e,r)?{working:!0,prompted:!1}:n;if(Rr(e,r)){let o=await Xe(t).catch(()=>r);if(Kn({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function Ha(t,e){return(await Gl(t,e)).working}var Dw=xE(kE),jw=[4e3,15e3,45e3],SE=2500,vE=5*6e4;async function zl(t,e,{midTurn:n=Ha,pollMs:r=SE,capMs:o=vE}={}){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 TE=6e4,AE=9e4,EE=2e3,_E=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,IE=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,RE=/\s--web\b/,Lw=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Vl(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Fw(t){let e=String(t||""),n=e.match(_E);if(!n)return null;let r=Vl(e,n.index);return RE.test(r)?null:{repo:r.match(Lw)?.[1]??null}}var $E=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function Uw(t){let e=String(t||""),n=e.match(IE);if(!n)return null;let r=Vl(e,n.index),o=Vl(e,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){$E.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Lw)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var OE="number,url,title,headRefName,createdAt";async function ME(t,e){let n=async a=>{Le("observer");let{stdout:i}=await Dw("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",OE],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...a}}).catch(c=>{throw un(c)&&dn(c),c});return JSON.parse(i||"[]")},r=a=>(a||[]).filter(i=>Date.parse(i.createdAt)>=e-TE),o=await ae({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 CE(t,e){if(e)return[e];let n=[];for(let r of Hw(t)){let o=await tt(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Te().catch(()=>[])){let o=await tt(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Hw(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 NE(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await CE(t,e)){let a;try{a=await ME(s,n)}catch{continue}for(let i of a){let c=await Rt(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 fn=new Set;function Ww(t,e,{delays:n=jw,turnEnd:r}={}){let o=Fw(e.command);if(!o)return null;let s=!!Uw(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(fn.has(a))return null;fn.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 NE(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:f,pr:d}=u,{link:m,created:h}=await Ht({repo:f,number:d.number,trackId:t.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!h&&!(m?.trackId===t.id&&await Do(f,d.number))?null:(await zl(e.box,e.agent,r),await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{fn.delete(a)}})();return c.catch(()=>fn.delete(a)),c}var PE="number,url,title,headRefName,state";function qw(t,e,{delays:n=jw,turnEnd:r,openedWait:o}={}){let s=Uw(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(fn.has(a))return null;fn.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 h=Hw(t);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let g=await tt(w).catch(()=>null);g&&!f.includes(g)&&f.push(g)}}if(l||=t.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(h,w)=>{let g=await Rt(c,h.number);return g&&g.trackId!==t.id?null:(g||await Ht({repo:c,number:h.number,trackId:t.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await jo(c,h.number),w&&((await L(t).catch(()=>[])).some(A=>A.type==="pr.state.changed"&&(A.payload?.change==="auto-armed"||A.payload?.change==="merged")&&A.payload?.number===h.number)||(await zl(e.box,e.agent,r),await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},m=null;for(let h of n){await new Promise(O=>setTimeout(O,h));let w=[];for(let O of f)try{Le("observer");let{stdout:F}=await Dw("gh",["pr","view",String(l),"--repo",O,"--json",PE],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await ae({slug:O})}}),T=JSON.parse(F||"null");T?.number&&w.push({slug:O,pr:T})}catch(F){un(F)&&dn(F)}let g=w.find(O=>O.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!g)continue;c=g.slug;let b=g.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(m=b,s.auto)return d(b,!0);continue}let S=await Rt(c,b.number);if(S&&S.trackId!==t.id)return null;S||await Ht({repo:c,number:b.number,trackId:t.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await zl(e.box,e.agent,r);let A=await L(t).catch(()=>[]);if(A.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="merged"&&O.payload?.number===b.number))return null;let C=O=>O.some(F=>F.type==="pr.state.changed"&&F.payload?.change==="opened"&&F.payload?.number===b.number),z=0;if(!C(A)){if(Fw(e.command)&&await Do(c,b.number)){let O=Date.now();z=O+1,await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:O,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await Rt(c,b.number))?.announced){let O=o?.capMs??AE,F=o?.pollMs??EE,T=Date.now()+O;for(;Date.now()<T&&(await new Promise(M=>setTimeout(M,F)),!C(await L(t).catch(()=>[]))););}}return await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),z),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await mn(c,b.number).catch(()=>{}),await pn(c,b.number).catch(()=>{}),await sn(c,b.number,"merged"),b}return m?d(m,!1):null}finally{fn.delete(a)}})();return i.catch(()=>fn.delete(a)),i}function Xl(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Kw(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Gw(t){return`job|${t.box}|id:${t.id}`}var Jl=new Map,Wa=new Map;async function Yl(t){return Jl.has(t.id)?Jl.get(t.id):(Wa.has(t.id)||Wa.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(Xl({role:o,sourceId:r.payload.sourceId})),n.keys.add(Xl({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(Kw({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(Gw({id:r.payload.id,box:r.boxId}));return Jl.set(t.id,n),n}).finally(()=>Wa.delete(t.id))),Wa.get(t.id))}async function zw(t,e){try{let n=(await Yl(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Xl(r);if(!n.has(o)&&(n.add(o),await $(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch)){let s=Sw(r.text);if(s){let{offered:a,toPr:i}=Tw(s),c;if(t.workspace?.kind==="multi"){let l=await Jo(t).catch(()=>[]);c=l.length?l.map(u=>({name:u.name,slug:u.repo??null})):void 0}await $(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:BE(),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 Vw(t,e){try{let n=await Yl(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=Kw(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await $(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),Ww(t,o),qw(t,o))}}catch{}}async function Jw(t,e){try{let n=await Yl(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Gw(o);n.keys.has(s)||(n.keys.add(s),await $(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var DE=/^Base directory for this skill:\s*(\S+)/,jE=/^Run the "([^"]+)" workflow\./,LE=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,FE=/^<local-command-(?:stdout|caveat)>/i,UE=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,HE=/^Continue from where you left off\.$/,WE=/^<task-notification>/i,qE=/<summary>([\s\S]*?)<\/summary>/i,KE=/<task-id>([\s\S]*?)<\/task-id>/i,GE=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,zE=/<status>([\s\S]*?)<\/status>/i,VE=/<result>([\s\S]*?)(?:<\/result>|$)/i;var Zl=new Set(["model","clear","compact","login","exit","quit"]);function Xw(t){return Zl.has(String(t||"").replace(/^\//,"").toLowerCase())}var JE=/^\/([a-z0-9:_-]+)$/i;function qa(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(DE);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(jE);if(r)return{kind:"skill",name:r[1]};let o=e.match(LE);if(o){let a=o[1].toLowerCase();return Zl.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(JE);if(s&&Zl.has(s[1].toLowerCase()))return{kind:"suppress"};if(FE.test(e))return{kind:"suppress"};if(HE.test(e))return{kind:"suppress"};if(UE.test(e))return{kind:"suppress"};if(WE.test(e)){let a=c=>XE(e.match(c)?.[1].trim()||"")||null,i=a(VE);return{kind:"task",summary:a(qE)||"",taskId:a(KE),toolUseId:a(GE),status:a(zE)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function XE(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function Yw(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 Zw(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var it=t=>t.payload?.ts??t.ts,YE=new Set(["opened","ci-fixed","reconciled","pushed"]),Qw=t=>`${t.repo??""}#${t.number}`;function ZE(t,e,n){let r=Qw(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 Xo(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 y of t||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let p=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(p))for(let v of p.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let E=v[0].split("/").pop().toLowerCase();f.has(E)||f.set(E,[]),f.get(E).push(y.payload?.ts??y.ts)}}let d=3*6e4,m=20*6e4,h=(y,p)=>!!(y?.name&&f.get(String(y.name).toLowerCase()))?.some(E=>E>=p-d&&E<=p+m);for(let y of t||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let p=ne(y.payload?.text);p&&(o.has(y.boxId)||o.set(y.boxId,[]),o.get(y.boxId).push(p))}else if(y.type==="turn.user"&&y.source==="orchestrator"){let p=ne(y.payload?.text);p&&(s.has(y.boxId)||s.set(y.boxId,new Set),s.get(y.boxId).add(p))}else if(y.type==="job.started"&&y.payload?.id)a.add(y.payload.id),(y.payload.kind||"agent")==="agent"&&y.payload.name&&c.add(y.payload.name);else if(y.type==="turn.user"&&y.payload?.note?.kind==="task"){let p=y.payload.note,v={status:p.status||null,summary:p.summary||null,resultHead:p.resultHead??null,ts:it(y)};p.toolUseId&&i.set(p.toolUseId,v);let E=String(p.summary||"").match(u);E&&l.set(E[1],v)}let w=new Map,g=new Map,b=null,S=new Map,A=new Map,C=new Map,z=new Map,O=new Map,F=new Map,T=new Map,M=6e4,W=new Map,st=(y,p)=>{let v=o.get(y);if(!v?.length)return!1;let E=ne(p),rt=s.get(y);return v.some(I=>{if(!E.startsWith(I))return!1;let R=ne(E.slice(I.length));return!!R&&!!rt?.has(R)})};for(let y of t||[]){let p=y.payload||{};switch(y.type){case"turn.user":{if(y.source==="hook"||!String(p.text||"").trim()&&!p.images?.length)break;{let v=p.note||qa(p.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let E=String(v.summary||"").match(u);if(!v.toolUseId&&E&&c.has(E[1]))break}if(v.kind==="command"&&Xw(v.command))break;v.kind!=="suppress"&&e.push({id:y.id,kind:"event",type:Zw(v),name:v.name??null,command:v.command??null,summary:v.summary??null,ts:it(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${it(y)}`)||y.source==="transcript"&&st(y.boxId,p.text))break;if(p.kind==="merge-notify"||p.kind==="ship-notify"){e.push({id:y.id,kind:"event",type:p.kind==="ship-notify"?"ship-notified":"merge-notified",text:p.text,ts:it(y),box:y.boxId,agent:y.provider});break}if(p.kind==="mock-picked"||p.kind==="ci-fix"||p.kind==="reconcile"||p.kind==="pr-go"||p.kind==="push-go"||p.kind==="spin-up-view"||p.kind==="prompt-answer")break;for(let v of S.values())v.status="stale";S.clear(),e.push({id:y.id,kind:"message",role:"user",text:p.text,ts:it(y),box:y.boxId,agent:y.provider,boxRole:p.boxRole,...y.actor==="orchestrator"?{orchestration:p.kind||!0}:{},...p.sourceId?{sourceId:p.sourceId}:{},...p.attachments?.length?{attachments:p.attachments}:{},...p.images?.length?{images:p.images}:{}});break}case"turn.agent":{if(p.model==="<synthetic>"&&/^No response requested\.$/.test(String(p.text||"").trim()))break;T.clear();let v=(p.images||[]).filter(R=>!h(R,it(y)));if(!String(p.text||"").trim()&&!v.length)break;if(y.provider==="cursor"){let R=`${y.boxId}|${p.text}`;if(n.has(R))break;n.add(R)}let E=y.provider==="codex"?(p.tools||[]).filter(R=>!ml.has(R)&&!Wo.has(R)):p.tools||[],rt=p.text,I=null;if($l.has(y.provider)){let R=Ol(String(p.text||""));R&&(rt=R.text,I={questions:R.questions})}e.push({id:y.id,kind:"message",role:"assistant",text:rt,ts:it(y),box:y.boxId,agent:y.provider,boxRole:p.boxRole,...I?{ask:I}:{},...E.length?{tools:E}:{},...p.model?{model:p.model}:{},...p.sourceId?{sourceId:p.sourceId}:{},...v.length?{images:v}:{}});break}case"job.started":{if(!p.id)break;let v=it(y),E=i.get(p.id);if(!E&&(p.kind||"agent")==="agent"&&p.name){let P=l.get(p.name);P&&P.ts>=v&&(E=P)}let rt={jobId:p.id,jobKind:p.kind||"agent",name:p.name||null,detail:p.detail||null,status:E?E.status&&E.status!=="completed"?"failed":"done":"running",startedTs:v,finishedTs:E?.ts??null,summary:E?.summary??null,resultHead:E?.resultHead??null},I=W.get(y.boxId),R=I?.jobs[I.jobs.length-1]?.startedTs;if(I&&R!=null&&v-R<=M)I.jobs.push(rt);else{let P={id:y.id,kind:"job-dock",dockId:p.id,ts:v,box:y.boxId,agent:y.provider,jobs:[rt]};W.set(y.boxId,P),e.push(P)}break}case"handoff.done":e.push(hn(y,"lead-handoff"));break;case"subtask.delegated":e.push({...hn(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":e.push(hn(y,"subtask-merged"));break;case"pr.state.changed":{if(!p.change)break;if(p.change==="opened"){for(let E of S.values())E.status="stale";S.clear()}let v=hn(y,`pr-${p.change}`);if(YE.has(p.change)&&p.number!=null){let E=ZE(w,g,p);if(w.size>=2){for(let I of g.values())for(let R of I)R.grouped=!0;v.groupPrs=[...w.values()];let rt=[v];for(let I of w.keys())g.set(I,rt)}else{let rt=g.get(E)??[];rt.push(v),g.set(E,rt)}}else if(p.change==="merged"||p.change==="closed"||p.change==="lost"){let E=Qw(p),rt=b&&!b.complete||w.size>=2;if(w.has(E))w.delete(E),g.delete(E);else if(p.number!=null)for(let I of[...w.keys()])I.endsWith(`#${p.number}`)&&(w.delete(I),g.delete(I));if(rt&&p.number!=null){(!b||b.complete)&&(b={id:y.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:it(y),ts:it(y),box:y.boxId,agent:y.provider},e.push(b)),b.prs.push({number:p.number,url:p.url??null,title:p.title??null,repo:p.repo??null,branch:p.branch??null,change:p.change,ts:it(y)}),b.remaining=[...w.values()],b.complete=w.size===0;break}}e.push(v);break}case"pr.offered":{if(!p.offerId)break;for(let E of S.values())E.status="stale";S.clear();let v={id:y.id,kind:"pr-offer",offerId:p.offerId,excerpt:p.excerpt||null,branch:p.branch||null,base:p.base||null,offered:Array.isArray(p.offered)&&p.offered.length?p.offered:null,toPr:p.toPr&&typeof p.toPr=="object"?{number:p.toPr.number??null}:null,repos:Array.isArray(p.repos)&&p.repos.length?p.repos:null,status:"open",ts:it(y),box:y.boxId,agent:y.provider};S.set(p.offerId,v),A.set(p.offerId,v),e.push(v);break}case"pr.offer.answered":{let v=A.get(p.offerId);v&&(v.status=p.action||"dismiss",v.actions=Array.isArray(p.actions)?p.actions:null,v.notify=p.notify===!0,S.delete(p.offerId));break}case"box.state.changed":{let v=p.change==="model"?"model-change":p.change;if(!v)break;let E=hn(y,v);v==="model-change"&&(E.to=p.model),e.push(E);break}case"view.state.changed":{if(p.change!=="created"||p.restarted)break;e.push({id:y.id,kind:"view-card",viewId:p.viewId,name:p.name,slug:p.slug,port:p.port,origin:p.origin,ts:it(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{e.push({id:y.id,kind:"view-spinup-failed",name:p.name||null,reason:p.reason||"no-view",ts:it(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(p.change==="picked"){e.push({id:y.id,kind:"mock-picked",mockId:p.mockId,name:p.name,group:p.group??null,version:p.version??null,siblings:p.siblings??[],ts:it(y),box:y.boxId,agent:y.provider});break}if(p.change!=="created")break;e.push({id:y.id,kind:"mock-card",mockId:p.mockId,name:p.name,group:p.group??null,ts:it(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(p.change!=="created")break;e.push({id:y.id,kind:"flow-card",flowId:p.flowId,name:p.name,stepCount:p.stepCount??null,mockIds:p.mockIds??[],ts:it(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(p.change!=="created")break;e.push({id:y.id,kind:"runbook-card",runbookId:p.runbookId,name:p.name,ts:it(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{e.push({id:y.id,kind:"ticket-card",provider:p.provider||"linear",key:p.key,url:p.url,title:p.title??null,linkSource:p.linkSource??null,ts:it(y),box:y.boxId,agent:y.provider});break}case"ticket.move.offered":{if(!p.offerId)break;for(let E of C.values())E.key===p.key&&(E.status="stale",C.delete(E.offerId));let v={id:y.id,kind:"ticket-offer",offerId:p.offerId,provider:p.provider||"linear",key:p.key,url:p.url??null,phase:p.phase??null,from:p.from??null,to:p.to?.name??null,prNumber:p.prNumber??null,status:"open",ts:it(y),box:y.boxId,agent:y.provider};C.set(p.offerId,v),z.set(p.offerId,v),e.push(v);break}case"ticket.move.answered":{let v=z.get(p.offerId);v&&(v.status=p.action==="moved"?"moved":"dismiss",C.delete(p.offerId));break}case"ticket.state.changed":{for(let v of C.values())v.key===p.key&&v.to===p.to&&(v.status="stale",C.delete(v.offerId));if(p.mode==="offer")break;e.push(hn(y,"ticket-state"));break}case"ticket.commented":{e.push({id:y.id,kind:"ticket-comment",key:p.key,url:p.url??null,excerpt:p.excerpt??null,ts:it(y),box:y.boxId,agent:y.provider});break}case"ticket.flow.changed":e.push(hn(y,"ticket-flow"));break;case"ticket.create.offered":{if(!p.proposalId)break;for(let E of O.values())E.status="stale";O.clear();let v={id:y.id,kind:"ticket-create-offer",proposalId:p.proposalId,provider:p.provider||"linear",epic:p.epic??null,issues:Array.isArray(p.issues)?p.issues:[],team:p.team??null,needsTeam:!!p.needsTeam,linkCurrentTrack:!!p.linkCurrentTrack,status:"open",ts:it(y),box:y.boxId,agent:y.provider};O.set(p.proposalId,v),F.set(p.proposalId,v),e.push(v);break}case"ticket.create.answered":{let v=F.get(p.proposalId);v&&(v.status=p.action==="created"?"created":"dismiss",v.createdCount=p.createdCount??null,v.failedCount=p.failedCount??null,O.delete(p.proposalId));break}case"ticket.created":{let v={items:Array.isArray(p.items)?p.items:[],failed:Array.isArray(p.failed)?p.failed:[],team:p.team??null},E=p.proposalId?F.get(p.proposalId):null;if(E){E.created=v;break}e.push({id:y.id,kind:"ticket-created",...v,ts:it(y),box:y.boxId,agent:y.provider});break}case"error":{if(!p.kind)break;e.push(hn(y,p.kind));break}case"provider.connected":{e.push({id:y.id,kind:"event",type:"provider-connected",provider:p.provider||y.provider,ts:it(y),at:it(y),box:y.boxId,agent:y.provider});break}case"prompt.answered":{if(p.control||!p.question)break;let v=p.groupId||p.promptId;if(!v)break;let E=T.get(v);E||(E={id:y.id,kind:"prompt",groupId:v,provider:y.provider,box:y.boxId,agent:y.provider,questionTotal:p.questionTotal??1,questions:[],ts:it(y)},T.set(v,E));let rt=p.questionIndex??0,I={index:rt,header:p.header||"",question:p.question,multiSelect:!!p.multiSelect,chosen:Array.isArray(p.chosen)?p.chosen:[]},R=E.questions.findIndex(P=>P.index===rt);R>=0?E.questions[R]=I:E.questions.push(I),E.questions.sort((P,et)=>P.index-et.index),E.questionTotal=Math.max(E.questionTotal,p.questionTotal??1),new Set(E.questions.map(P=>P.index)).size>=E.questionTotal&&(e.push(E),T.delete(v));break}default:break}}return t_(e.sort((y,p)=>(y.ts??0)-(p.ts??0)))}var QE=2*6e4;function t_(t){let e=new Map,n=[];for(let r of t){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=e.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=QE){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 Ql(t,e=null){let n=[];for(let o of t||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:e_(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(e??Xo(t)).filter(o=>o.kind==="message");return fh(n,r)}function e_(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Ar(t.command)?e:an(t.command,t.description||"")}function hn(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:it(t),ts:it(t)}}function n_(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 tu=new Map;async function r_(t,e){let n=`${t.id}:${e.name}`;if(!tu.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);tu.set(n,o)}return tu.get(n)}async function tg(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Fn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await r_(t,e),o=n_(await Mt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await $(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:le(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await $(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}import Ka from"node:fs";import nu from"node:path";import o_ from"node:os";var s_=process.env.BOSTRAT_STATE_DIR||nu.join(process.env.HOME||o_.tmpdir(),".bostrat"),ng=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,a_="https://cursor.com/api/dashboard/get-filtered-usage-events",eg=100;async function i_({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(a_,{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:eg})});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<eg)break}return r}function c_(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 l_=5*6e4,wn={at:0,rows:null,error:null},Ga=()=>nu.join(s_,"cursor-usage.json");function rg(){try{return JSON.parse(Ka.readFileSync(Ga(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function u_(t){try{Ka.mkdirSync(nu.dirname(Ga()),{recursive:!0});let e=Ga()+".tmp";Ka.writeFileSync(e,JSON.stringify(t)),Ka.renameSync(e,Ga())}catch{}}var d_=5e3;async function f_({fetchImpl:t,now:e=Date.now()}={}){if(wn.rows&&e-wn.at<l_)return wn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=rg(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await i_({sinceMs:o,cookie:n,fetchImpl:t});wn={at:e,rows:s.map(c_).filter(Boolean),error:null}}catch(s){wn={at:e,rows:null,error:String(s?.message||s)}}return wn.rows}var ru=()=>({enabled:ng(),lastPollAt:wn.at||null,error:wn.error}),eu=new Map;async function p_(t,e){let n=`${t.id}:${e.name}`;if(!eu.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);eu.set(n,o)}return eu.get(n)}async function og(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!ng())return;let r=await f_({fetchImpl:n});if(!r||!r.length)return;let o=await xh(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await p_(t,e),a=rg(),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=le(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await $(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}u_({sinceMs:c,seen:[...i].slice(-d_)})}catch{}}import{execFile as y_}from"node:child_process";import{promisify as b_}from"node:util";import sg from"node:fs/promises";import ag from"node:path";import m_ from"node:os";var ig=process.env.BOSTRAT_STATE_DIR||ag.join(process.env.HOME||m_.tmpdir(),".bostrat"),h_=ag.join(ig,"github-latency.ndjson"),w_=100,za=[],ou=new Map;function Va({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};za.push(l),za.length>w_&&za.shift();let u=ou.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)),ou.set(t,u),g_(l),l}async function g_(t){try{await sg.mkdir(ig,{recursive:!0}),await sg.appendFile(h_,JSON.stringify(t)+`
147
- `)}catch{}}function cg(){let t={};for(let[e,n]of ou)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:za.slice(-20)}}async function k_(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([Pr("git",["-C",t,"rev-parse","HEAD"]),Pr("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await $({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var Pr=b_(y_),x_={timeout:3e4,maxBuffer:8*1024*1024};async function Jt(t,e,n={}){let{caller:r="prs",...o}=n,s=await ae({cwd:t});Le(r);try{let{stdout:a}=await Pr("gh",e,{cwd:t,...x_,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw un(a)&&dn(a),a}}var Ja=null;async function iu(){if(Ja&&Date.now()-Ja.at<6e4)return Ja.data;let t;try{Le("auth");let e=await Pr("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 co();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Ja={at:Date.now(),data:t},t}async function S_(t){try{let{stdout:e}=await Pr("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function ug(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 su(t){let e=(t||[]).map(ug),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 cu="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",dg=cu.replace("statusCheckRollup,","");function lu(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function fg(t,e){if(!e)return null;let n=await tt(t);if(!n)return null;try{let r=await Jt(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 pg(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 mg(t,e){let n=await fg(t,e);return n===null?{...su([]),unavailable:!0}:su(pg(n))}function Xa(t,e=su(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 v_=9e4,Nr=new Map;function T_(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Nr.delete(s);continue}r.add(s),Nr.has(s)||Nr.set(s,n),o.mergeabilityPending=n-Nr.get(s)<v_}for(let o of Nr.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Nr.delete(o);return e}async function Br(t,e){let n=await Fe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Jt(n.path,["pr","view",String(r),"--json",cu]);return Xa(JSON.parse(o))}catch(o){if(!lu(o))throw o;let s=await Jt(n.path,["pr","view",String(r),"--json",dg]),a=JSON.parse(s);return Xa(a,await mg(n.path,a.headRefOid))}}var lg=(t,e)=>t.number-e.number;async function Zo(t,{caller:e="prs"}={}){try{let n=await Jt(t,["pr","list","--json",cu,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>Xa(r)).sort(lg)}catch(n){if(!lu(n))throw n;let r=await Jt(t,["pr","list","--json",dg,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Xa(o,await mg(t,o.headRefOid))))).sort(lg)}}var A_=3e4,E_=1500,Yo=new Map;function xe(){Yo.clear()}async function __(t,e){if(!t)return e;for(let n of e){let r=await Rt(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 Ta(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ea(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function hg(){let t=await iu();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Te())await S_(a.path)&&e.push(a);let n=Date.now(),r=Ua(n)>0,o=await Promise.all(e.map(async a=>{let i=Yo.get(a.path),c=i?.pending?E_:A_;if(i&&(r||n-i.at<c))return i.entry;try{let l=await tt(a.path).catch(()=>null),u=T_(a.path,await Zo(a.path,{caller:"tab"}),n);if(await __(l,u),i?.entry?.prs)for(let m of u)i.entry.prs.find(w=>w.number===m.number)?.checks?.pending>0&&m.checks.total>0&&m.checks.pending===0&&Va({change:m.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:m.number,githubAt:m.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(m=>m.mergeabilityPending||m.checks.pending>0||m.autoMergeArmed);return Yo.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:ke(l)}}})),s=new Set(e.map(a=>a.path));for(let a of Yo.keys())s.has(a)||Yo.delete(a);return{auth:t,repos:o}}async function Fe(t){let e=(await Te()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function zn(t){return ao(t)}var Qo=new Set(["squash","merge","rebase"]);async function Dr(t,e,{method:n="squash"}={}){let r=await Fe(t);if(!Qo.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 ae({cwd:r.path})).GH_TOKEN){let a=await Bi().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Jt(r.path,s)}catch(a){throw new Error(ke(a))}finally{xe()}return{ok:!0,number:o}}async function ts(t,e){let n=await Fe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Jt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(ke(s))}finally{xe()}let o=await tt(n.path).catch(()=>null);return o&&await Gm(o,r).catch(()=>{}),{ok:!0,number:r}}async function uu(t,e){let n=await Fe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Jt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(ke(o))}finally{xe()}return{ok:!0,number:r}}var I_=2500,R_=9e4,$_=t=>new Promise(e=>setTimeout(e,t));function du(t){return t.green&&!t.isDraft}function O_(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 wg(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:du(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function gg(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var au=new Map;function yg(){let t={};for(let[e,n]of au)t[e]={...n};return t}async function bg(t,{method:e="merge",onMerged:n=null}={}){let r=await Fe(t);if(!Qo.has(e))throw new Error(`invalid merge method: ${e}`);if(au.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Zo(r.path)).filter(du).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 au.set(r.path,s),C_(s,o,{listPrs:()=>Zo(r.path),merge:a=>Dr(r.path,a,{method:e}),sleep:$_,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function M_(t,e){let n=Date.now()+(e.settleMaxMs??R_),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(I_)}}async function C_(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 M_(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: ${ke(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(!du(a)){t.skipped.push({number:a.number,title:a.title,reason:O_(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:ke(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),xe()}}async function fu(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Pr("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${ke(l)}`)}await k_(t);let o=n||"";if((await ae({cwd:t})).GH_TOKEN){let l=await Bi().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 Jt(t,s)}catch(l){throw new Error(ke(l))}finally{xe()}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 kg(t,e){let n=await Fe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Jt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(ke(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!lu(i))throw i;let c=await Jt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await fg(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(ke(i));o=pg(l).map(ug).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=N_(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Jt(n.path,["run","view",c,"--log-failed"]).then(u=>P_(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function N_(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function P_(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
138
+ `;import{execFile as rE}from"node:child_process";import{promisify as oE}from"node:util";var jl=oE(rE),cw=64*1024*1024;async function mt(t,e,{input:n}={}){let r=jl("git",["-C",t,...e],{maxBuffer:cw});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Oa(t){if(!t)return!1;try{let{stdout:e}=await mt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function lw(t){let{stdout:e}=await mt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function sE(t){try{return await mt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Ko(t,e="bostrat: subtask snapshot"){let{stdout:n}=await mt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>jl("git",["-C",t,...a],{maxBuffer:cw,env:o,...i});try{let a=await sE(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 jl("rm",["-f",r])}catch{}}}async function Ll(t,e){let{stdout:n}=await mt(t,["status","--porcelain"]);return n.trim()?(await mt(t,["add","-A"]),await mt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function Fl(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await mt(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 Ul(t,e){if(!e)return 0;try{let{stdout:n}=await mt(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Or(t,e,n,r){return await mt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function uw(t,e){try{return await mt(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function dw(t,e,n){return await mt(t,["worktree","add",e,n]),e}async function Ca(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await mt(t,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(e.trim())return e.trim()}catch{}for(let[e,n]of[["refs/remotes/origin/main","origin/main"],["refs/remotes/origin/master","origin/master"],["refs/heads/main","main"],["refs/heads/master","master"]])try{return await mt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function fw(t){await mt(t,["init","-q"]),await mt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Xt(t,e){try{await mt(t,["worktree","remove","--force",e])}catch{try{await mt(t,["worktree","prune"])}catch{}}}function aE(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 Hl(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Wl(t,e,n,r){let{stdout:o}=await mt(t,Hl(["diff","--shortstat",e,n],r));return aE(o)}async function ql(t,e,n,r){let{stdout:o}=await mt(t,Hl(["diff","--name-only",e,n],r));return o.split(`
140
+ `).map(s=>s.trim()).filter(Boolean)}async function Kl(t,e,n,r){let{stdout:o}=await mt(t,Hl(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Dl(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Dl(t,o,["--3way","--whitespace=nowarn"]),a=await iE(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Dl(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Dl(t,e,n){try{return await mt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function iE(t){try{let{stdout:e}=await mt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
141
+ `).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import cE from"node:fs/promises";import lE from"node:os";import pw from"node:path";var Ma=t=>new Promise(e=>setTimeout(e,t)),uE=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,dE=300,gw=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 4.8"},{alias:"sonnet",label:"Sonnet 4.6"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],fE=new Set([...gw.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),pE=[{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.4",label:"GPT-5.4"},{alias:"gpt-5.4-mini",label:"GPT-5.4 Mini"},{alias:"gpt-5.3-codex",label:"Codex 5.3"},{alias:"gpt-5.2",label:"GPT-5.2"}],yw=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),bw=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-4-8",label:"Opus 4.8"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}];function kw(t){return t==="codex"?pE:t==="cursor"?bw:gw}function mw(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 hw(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 mE(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 ww(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 xw(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&&!yw.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=bw.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!fE.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Go(t,e,{timeoutMs:n=uE}={}){let r=Date.now()+n;for(;;){let o=await ht(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await Ma(dE)}}async function hE(t,e){return await xt(t,`/model ${e.model}`,Ee("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function wE(t,e){await Fs(t),await xt(t,"/model",{enter:!1}),await Ma(250),await bt(t,["Enter"],{kind:"model-switch"});let n=await Go(t,mw);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await bt(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=mE(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await xt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await Go(t,i=>hw(i)||!mw(i)&&o(i)&&{done:o(i)});if(!s)throw await bt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?yw.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 bt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await xt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Go(t,i=>!hw(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 gE(){let t=pw.join(process.env.CURSOR_CONFIG_DIR||pw.join(lE.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await cE.readFile(t,"utf8"))?.model||null}catch{return null}}async function yE(t,e){await Fs(t),await Hs(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await Go(t,ww),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 Go(t,ww,{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 bt(t,["Down"],{kind:"model-switch"}),await Ma(250)}if(!n||!s||!o(s)){await bt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await bt(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 bt(t,["Enter"],{kind:"model-switch"}),await Ma(800);let a=await gE();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 Sw(t,e,n){return t==="codex"?wE(e,n):t==="cursor"?yE(e,n):hE(e,n)}var Kt=t=>t||null;function Pa(t){let e=t||{},n=bE(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function bE(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Kt(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:Kt(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:Kt(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:Kt(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:Kt(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:Kt(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:Kt(t.agent),payload:{...Cr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Kt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Cr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{change:"subtask-resumed",...Cr(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",...Cr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Cr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Cr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var Na=Promise.resolve(),Vl=t=>(Na=Na.then(t).catch(()=>{}),Na),Tw=()=>Na;function Aw(t,e){let n=Pa(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Vl(()=>R(r,n))}var vw=new Set;async function Ew(t){if(!(!t?.id||vw.has(t.id))){vw.add(t.id);try{let e=await D(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=Pa(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function Cr(t){let{type:e,at:n,...r}=t;return r}var Gl=new Map;function _w(t,e,n){!n?.promptId||Gl.get(t.id)===n.promptId||(Gl.set(t.id,n.promptId),Vl(()=>R(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Kt(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 zl(t,e,n,r={}){Gl.delete(t.id),Vl(()=>R(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Kt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as YE}from"node:crypto";var Rw=/(?<!\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,$w=/(?<!\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,Ow=/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,Cw=/(?<!\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,kE=new RegExp(`\\b(?:${Rw}|${$w}|${Ow}|${Cw})\\b`,"i"),xE=/\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,SE=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,vE=90;function TE(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function Mw(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of TE(n).reverse()){if(!kE.test(r))continue;let o=r.endsWith("?")&&r.length<=vE&&SE.test(r);if(xE.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var Nw=["commit","rebase","push","pr"],AE=new RegExp(`\\b(?:${Rw})\\b`,"i"),EE=new RegExp(`\\b(?:${Cw})\\b`,"i"),_E=new RegExp(`\\b(?:${$w})\\b`,"i"),IE=new RegExp(`\\b(?:${Ow})\\b`,"i"),RE=/\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 Pw(t){let e=String(t||""),n=[];AE.test(e)&&n.push("commit"),EE.test(e)&&n.push("rebase"),_E.test(e)&&n.push("push");let r=RE.test(e);!r&&IE.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 Ba(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 $E=3e4;function Bw(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<=$E&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Dw(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 Iw={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function jw(t){let e=(Array.isArray(t)?t:[]).filter(r=>Iw[r]).map(r=>Iw[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 PE}from"node:child_process";import{promisify as BE}from"node:util";import OE from"node:fs";import Jl from"node:fs/promises";import Hw from"node:path";import CE from"node:os";var Ww=process.env.BOSTRAT_STATE_DIR||Hw.join(process.env.HOME||CE.tmpdir(),".bostrat"),Da=Hw.join(Ww,"github-metrics.json"),qw=60*6e4,Lw=5*6e4,ME=6e4,Mr=Date.now(),De=new Map,un=null,Vn=0,Fw=0,ja=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(OE.readFileSync(Da,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<qw?(Mr=t.windowStart,De=new Map(Object.entries(t.byCaller||{})),un=t.lastHour||null):ja(new Map(Object.entries(t.byCaller||{})))&&(un={start:t.windowStart,total:ja(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var Uw=Promise.resolve();function Kw(t,e=!1){if(!e&&t-Fw<ME)return;Fw=t;let n=JSON.stringify({windowStart:Mr,byCaller:Object.fromEntries(De),lastHour:un});Uw=Uw.then(()=>Jl.mkdir(Ww,{recursive:!0})).then(()=>Jl.writeFile(`${Da}.tmp`,n)).then(()=>Jl.rename(`${Da}.tmp`,Da)).catch(()=>{})}function Gw(t){t-Mr<qw||(un={start:Mr,total:ja(De),byCaller:Object.fromEntries(De)},un.total&&console.log(`[github] ${un.total} calls last hour`,un.byCaller),Mr=t,De=new Map,Kw(t,!0))}function je(t="other",e=Date.now()){Gw(e),De.set(t,(De.get(t)||0)+1),Kw(e)}function Vw(t=Date.now()){return Gw(t),{windowStart:Mr,total:ja(De),byCaller:Object.fromEntries(De),lastHour:un,backoffUntil:Vn>t?Vn:null}}var NE=/rate limit|too quickly|abuse detection|HTTP 429/i;function dn(t){return NE.test(String(t?.message||t||""))}function fn(t,e=Date.now()){return Vn<=e&&console.warn(`[github] rate limited \u2014 backing off ${Lw/6e4} min:`,String(t?.message||t||"").slice(0,200)),Vn=Math.max(Vn,e+Lw),Vn}function La(t=Date.now()){return Math.max(0,Vn-t)}async function Xl(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await ht(t,{lines:Tt}).catch(()=>"");if(!Ze(e,r))return qf(e,r)?{working:!0,prompted:!1}:n;if(Rr(e,r)){let o=await Xe(t).catch(()=>r);if(Gn({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function Fa(t,e){return(await Xl(t,e)).working}var zw=BE(PE),Jw=[4e3,15e3,45e3],DE=2500,jE=5*6e4;async function Yl(t,e,{midTurn:n=Fa,pollMs:r=DE,capMs:o=jE}={}){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 LE=6e4,FE=9e4,UE=2e3,HE=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,WE=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,qE=/\s--web\b/,Xw=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Zl(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Yw(t){let e=String(t||""),n=e.match(HE);if(!n)return null;let r=Zl(e,n.index);return qE.test(r)?null:{repo:r.match(Xw)?.[1]??null}}var KE=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function Zw(t){let e=String(t||""),n=e.match(WE);if(!n)return null;let r=Zl(e,n.index),o=Zl(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("-")){KE.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Xw)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var GE="number,url,title,headRefName,createdAt";async function VE(t,e){let n=async a=>{je("observer");let{stdout:i}=await zw("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",GE],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...a}}).catch(c=>{throw dn(c)&&fn(c),c});return JSON.parse(i||"[]")},r=a=>(a||[]).filter(i=>Date.parse(i.createdAt)>=e-LE),o=await ae({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 zE(t,e){if(e)return[e];let n=[];for(let r of Qw(t)){let o=await tt(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await ve().catch(()=>[])){let o=await tt(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Qw(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 JE(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await zE(t,e)){let a;try{a=await VE(s,n)}catch{continue}for(let i of a){let c=await $t(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 pn=new Set;function tg(t,e,{delays:n=Jw,turnEnd:r}={}){let o=Yw(e.command);if(!o)return null;let s=!!Zw(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(pn.has(a))return null;pn.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(g=>setTimeout(g,l));let u=await JE(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await Wt({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 Po(f,d.number))?null:(await Yl(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{pn.delete(a)}})();return c.catch(()=>pn.delete(a)),c}var XE="number,url,title,headRefName,state";function eg(t,e,{delays:n=Jw,turnEnd:r,openedWait:o}={}){let s=Zw(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(pn.has(a))return null;pn.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=Qw(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let g of m){let y=await tt(g).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,g)=>{let y=await $t(c,m.number);return y&&y.trackId!==t.id?null:(y||await Wt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Bo(c,m.number),g&&((await D(t).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===m.number)||(await Yl(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($=>setTimeout($,m));let g=[];for(let $ of f)try{je("observer");let{stdout:j}=await zw("gh",["pr","view",String(l),"--repo",$,"--json",XE],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await ae({slug:$})}}),v=JSON.parse(j||"null");v?.number&&g.push({slug:$,pr:v})}catch(j){dn(j)&&fn(j)}let y=g.find($=>$.pr.state==="MERGED")??(g.length===1?g[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 $t(c,b.number);if(S&&S.trackId!==t.id)return null;S||await Wt({repo:c,number:b.number,trackId:t.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await Yl(e.box,e.agent,r);let E=await D(t).catch(()=>[]);if(E.some($=>$.type==="pr.state.changed"&&$.payload?.change==="merged"&&$.payload?.number===b.number))return null;let M=$=>$.some(j=>j.type==="pr.state.changed"&&j.payload?.change==="opened"&&j.payload?.number===b.number),z=0;if(!M(E)){if(Yw(e.command)&&await Po(c,b.number)){let $=Date.now();z=$+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:$,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await $t(c,b.number))?.announced){let $=o?.capMs??FE,j=o?.pollMs??UE,v=Date.now()+$;for(;Date.now()<v&&(await new Promise(C=>setTimeout(C,j)),!M(await D(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:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await hn(c,b.number).catch(()=>{}),await mn(c,b.number).catch(()=>{}),await an(c,b.number,"merged"),b}return p?d(p,!1):null}finally{pn.delete(a)}})();return i.catch(()=>pn.delete(a)),i}function tu(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function ng(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function rg(t){return`job|${t.box}|id:${t.id}`}var Ql=new Map,Ua=new Map;async function eu(t){return Ql.has(t.id)?Ql.get(t.id):(Ua.has(t.id)||Ua.set(t.id,D(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(tu({role:o,sourceId:r.payload.sourceId})),n.keys.add(tu({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(ng({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(rg({id:r.payload.id,box:r.boxId}));return Ql.set(t.id,n),n}).finally(()=>Ua.delete(t.id))),Ua.get(t.id))}async function og(t,e){try{let n=(await eu(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=tu(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=Mw(r.text);if(s){let{offered:a,toPr:i}=Pw(s),c;if(t.workspace?.kind==="multi"){let l=await Vo(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:YE(),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 sg(t,e){try{let n=await eu(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=ng(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}}),tg(t,o),eg(t,o))}}catch{}}async function ag(t,e){try{let n=await eu(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=rg(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 ZE=/^Base directory for this skill:\s*(\S+)/,QE=/^Run the "([^"]+)" workflow\./,t_=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,e_=/^<local-command-(?:stdout|caveat)>/i,n_=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,r_=/^Continue from where you left off\.$/,o_=/^<task-notification>/i,s_=/<summary>([\s\S]*?)<\/summary>/i,a_=/<task-id>([\s\S]*?)<\/task-id>/i,i_=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,c_=/<status>([\s\S]*?)<\/status>/i,l_=/<result>([\s\S]*?)(?:<\/result>|$)/i;var nu=new Set(["model","clear","compact","login","exit","quit"]);function ig(t){return nu.has(String(t||"").replace(/^\//,"").toLowerCase())}var u_=/^\/([a-z0-9:_-]+)$/i;function Ha(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(ZE);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(QE);if(r)return{kind:"skill",name:r[1]};let o=e.match(t_);if(o){let a=o[1].toLowerCase();return nu.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(u_);if(s&&nu.has(s[1].toLowerCase()))return{kind:"suppress"};if(e_.test(e))return{kind:"suppress"};if(r_.test(e))return{kind:"suppress"};if(n_.test(e))return{kind:"suppress"};if(o_.test(e)){let a=c=>d_(e.match(c)?.[1].trim()||"")||null,i=a(l_);return{kind:"task",summary:a(s_)||"",taskId:a(a_),toolUseId:a(i_),status:a(c_)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function d_(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function cg(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 lg(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var at=t=>t.payload?.ts??t.ts,f_=new Set(["opened","ci-fixed","reconciled","pushed"]),ug=t=>`${t.repo??""}#${t.number}`;function p_(t,e,n){let r=ug(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 zo(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 w of t||[]){if(w.type!=="tool.called"||w.source!=="transcript")continue;let h=String(w.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let T of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let _=T[0].split("/").pop().toLowerCase();f.has(_)||f.set(_,[]),f.get(_).push(w.payload?.ts??w.ts)}}let d=3*6e4,p=20*6e4,m=(w,h)=>!!(w?.name&&f.get(String(w.name).toLowerCase()))?.some(_=>_>=h-d&&_<=h+p);for(let w of t||[])if(w.type==="turn.retracted"){r.add(`${w.boxId}|${w.payload?.saidTs}`);let h=qt(w.payload?.text);h&&(o.has(w.boxId)||o.set(w.boxId,[]),o.get(w.boxId).push(h))}else if(w.type==="turn.user"&&w.source==="orchestrator"){let h=qt(w.payload?.text);h&&(s.has(w.boxId)||s.set(w.boxId,new Set),s.get(w.boxId).add(h))}else if(w.type==="job.started"&&w.payload?.id)a.add(w.payload.id),(w.payload.kind||"agent")==="agent"&&w.payload.name&&c.add(w.payload.name);else if(w.type==="turn.user"&&w.payload?.note?.kind==="task"){let h=w.payload.note,T={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:at(w)};h.toolUseId&&i.set(h.toolUseId,T);let _=String(h.summary||"").match(u);_&&l.set(_[1],T)}let g=new Map,y=new Map,b=null,S=new Map,E=new Map,M=new Map,z=new Map,$=new Map,j=new Map,v=new Map,C=()=>{for(let w of v.values())w.questions.length&&e.push(w);v.clear()},H=6e4,nt=new Map,ot=(w,h)=>{let T=o.get(w);if(!T?.length)return!1;let _=qt(h),O=s.get(w);return T.some(I=>{if(!_.startsWith(I))return!1;let A=qt(_.slice(I.length));return!!A&&!!O?.has(A)})};for(let w of t||[]){let h=w.payload||{};switch(w.type){case"turn.user":{if(w.source==="hook"||!String(h.text||"").trim()&&!h.images?.length)break;{let T=h.note||Ha(h.text);if(T){if(T.kind==="task"){if(T.toolUseId&&a.has(T.toolUseId))break;let _=String(T.summary||"").match(u);if(!T.toolUseId&&_&&c.has(_[1]))break}if(T.kind==="command"&&ig(T.command))break;T.kind!=="suppress"&&e.push({id:w.id,kind:"event",type:lg(T),name:T.name??null,command:T.command??null,summary:T.summary??null,ts:at(w),box:w.boxId,agent:w.provider});break}}if(r.has(`${w.boxId}|${at(w)}`)||w.source==="transcript"&&ot(w.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){e.push({id:w.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:at(w),box:w.boxId,agent:w.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")break;for(let T of S.values())T.status="stale";S.clear(),e.push({id:w.id,kind:"message",role:"user",text:h.text,ts:at(w),box:w.boxId,agent:w.provider,boxRole:h.boxRole,...w.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 T=(h.images||[]).filter(A=>!m(A,at(w)));if(!String(h.text||"").trim()&&!T.length)break;if(w.provider==="cursor"){let A=`${w.boxId}|${h.text}`;if(n.has(A))break;n.add(A)}let _=w.provider==="codex"?(h.tools||[]).filter(A=>!yl.has(A)&&!Uo.has(A)):h.tools||[],O=h.text,I=null;if(Nl.has(w.provider)){let A=Pl(String(h.text||""));A&&(O=A.text,I={questions:A.questions})}e.push({id:w.id,kind:"message",role:"assistant",text:O,ts:at(w),box:w.boxId,agent:w.provider,boxRole:h.boxRole,...I?{ask:I}:{},..._.length?{tools:_}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...T.length?{images:T}:{}});break}case"job.started":{if(!h.id)break;let T=at(w),_=i.get(h.id);if(!_&&(h.kind||"agent")==="agent"&&h.name){let q=l.get(h.name);q&&q.ts>=T&&(_=q)}let O={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:_?_.status&&_.status!=="completed"?"failed":"done":"running",startedTs:T,finishedTs:_?.ts??null,summary:_?.summary??null,resultHead:_?.resultHead??null},I=nt.get(w.boxId),A=I?.jobs[I.jobs.length-1]?.startedTs;if(I&&A!=null&&T-A<=H)I.jobs.push(O);else{let q={id:w.id,kind:"job-dock",dockId:h.id,ts:T,box:w.boxId,agent:w.provider,jobs:[O]};nt.set(w.boxId,q),e.push(q)}break}case"handoff.done":e.push(wn(w,"lead-handoff"));break;case"subtask.delegated":e.push({...wn(w,"subtask-delegated"),to:w.boxId,toAgent:w.provider});break;case"subtask.merged":e.push(wn(w,"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 T=wn(w,`pr-${h.change}`);if(f_.has(h.change)&&h.number!=null){let _=p_(g,y,h);if(g.size>=2){for(let I of y.values())for(let A of I)A.grouped=!0;T.groupPrs=[...g.values()];let O=[T];for(let I of g.keys())y.set(I,O)}else{let O=y.get(_)??[];O.push(T),y.set(_,O)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let _=ug(h),O=b&&!b.complete||g.size>=2;if(g.has(_))g.delete(_),y.delete(_);else if(h.number!=null)for(let I of[...g.keys()])I.endsWith(`#${h.number}`)&&(g.delete(I),y.delete(I));if(O&&h.number!=null){(!b||b.complete)&&(b={id:w.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:at(w),ts:at(w),box:w.boxId,agent:w.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:at(w)}),b.remaining=[...g.values()],b.complete=g.size===0;break}}e.push(T);break}case"pr.offered":{if(!h.offerId)break;for(let _ of S.values())_.status="stale";S.clear();let T={id:w.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(w),box:w.boxId,agent:w.provider};S.set(h.offerId,T),E.set(h.offerId,T),e.push(T);break}case"pr.offer.answered":{let T=E.get(h.offerId);T&&(T.status=h.action||"dismiss",T.actions=Array.isArray(h.actions)?h.actions:null,T.notify=h.notify===!0,S.delete(h.offerId));break}case"box.state.changed":{let T=h.change==="model"?"model-change":h.change;if(!T)break;let _=wn(w,T);T==="model-change"&&(_.to=h.model),e.push(_);break}case"view.state.changed":{if(h.change!=="created"||h.restarted)break;e.push({id:w.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:at(w),box:w.boxId,agent:w.provider});break}case"view.spinup.failed":{e.push({id:w.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:at(w),box:w.boxId,agent:w.provider});break}case"mock.state.changed":{if(h.change==="picked"){e.push({id:w.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:at(w),box:w.boxId,agent:w.provider});break}if(h.change!=="created")break;e.push({id:w.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:at(w),box:w.boxId,agent:w.provider});break}case"flow.state.changed":{if(h.change!=="created")break;e.push({id:w.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:at(w),box:w.boxId,agent:w.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;e.push({id:w.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:at(w),box:w.boxId,agent:w.provider});break}case"ticket.linked":{e.push({id:w.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:at(w),box:w.boxId,agent:w.provider});break}case"ticket.move.offered":{if(!h.offerId)break;for(let _ of M.values())_.key===h.key&&(_.status="stale",M.delete(_.offerId));let T={id:w.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(w),box:w.boxId,agent:w.provider};M.set(h.offerId,T),z.set(h.offerId,T),e.push(T);break}case"ticket.move.answered":{let T=z.get(h.offerId);T&&(T.status=h.action==="moved"?"moved":"dismiss",M.delete(h.offerId));break}case"ticket.state.changed":{for(let T of M.values())T.key===h.key&&T.to===h.to&&(T.status="stale",M.delete(T.offerId));if(h.mode==="offer")break;e.push(wn(w,"ticket-state"));break}case"ticket.commented":{e.push({id:w.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:at(w),box:w.boxId,agent:w.provider});break}case"ticket.flow.changed":e.push(wn(w,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let _ of $.values())_.status="stale";$.clear();let T={id:w.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(w),box:w.boxId,agent:w.provider};$.set(h.proposalId,T),j.set(h.proposalId,T),e.push(T);break}case"ticket.create.answered":{let T=j.get(h.proposalId);T&&(T.status=h.action==="created"?"created":"dismiss",T.createdCount=h.createdCount??null,T.failedCount=h.failedCount??null,$.delete(h.proposalId));break}case"ticket.created":{let T={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},_=h.proposalId?j.get(h.proposalId):null;if(_){_.created=T;break}e.push({id:w.id,kind:"ticket-created",...T,ts:at(w),box:w.boxId,agent:w.provider});break}case"error":{if(!h.kind)break;e.push(wn(w,h.kind));break}case"provider.connected":{e.push({id:w.id,kind:"event",type:"provider-connected",provider:h.provider||w.provider,ts:at(w),at:at(w),box:w.boxId,agent:w.provider});break}case"prompt.answered":{if(h.control||!h.question)break;let T=h.groupId||h.promptId;if(!T)break;let _=v.get(T);_||(_={id:w.id,kind:"prompt",groupId:T,provider:w.provider,box:w.boxId,agent:w.provider,questionTotal:h.questionTotal??1,questions:[],ts:at(w)},v.set(T,_));let O=h.questionIndex??0,I={index:O,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},A=_.questions.findIndex(q=>q.index===O&&qt(q.question)===qt(I.question));A>=0?_.questions[A]=I:_.questions.push(I),_.questions.sort((q,dt)=>q.index-dt.index),_.questionTotal=Math.max(_.questionTotal,h.questionTotal??1),_.questions.length>=_.questionTotal&&(e.push(_),v.delete(T));break}default:break}}return h_(e.sort((w,h)=>(w.ts??0)-(h.ts??0)))}var m_=2*6e4;function h_(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<=m_){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 ru(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:w_(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??zo(t)).filter(o=>o.kind==="message");return vh(n,r)}function w_(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Ar(t.command)?e:cn(t.command,t.description||"")}function wn(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)}}function g_(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 ou=new Map;async function y_(t,e){let n=`${t.id}:${e.name}`;if(!ou.has(n)){let r=await D(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);ou.set(n,o)}return ou.get(n)}async function dg(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Un(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await y_(t,e),o=g_(await Nt(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:le(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 Wa from"node:fs";import au from"node:path";import b_ from"node:os";var k_=process.env.BOSTRAT_STATE_DIR||au.join(process.env.HOME||b_.tmpdir(),".bostrat"),pg=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,x_="https://cursor.com/api/dashboard/get-filtered-usage-events",fg=100;async function S_({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(x_,{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:fg})});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<fg)break}return r}function v_(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 T_=5*6e4,gn={at:0,rows:null,error:null},qa=()=>au.join(k_,"cursor-usage.json");function mg(){try{return JSON.parse(Wa.readFileSync(qa(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function A_(t){try{Wa.mkdirSync(au.dirname(qa()),{recursive:!0});let e=qa()+".tmp";Wa.writeFileSync(e,JSON.stringify(t)),Wa.renameSync(e,qa())}catch{}}var E_=5e3;async function __({fetchImpl:t,now:e=Date.now()}={}){if(gn.rows&&e-gn.at<T_)return gn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=mg(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await S_({sinceMs:o,cookie:n,fetchImpl:t});gn={at:e,rows:s.map(v_).filter(Boolean),error:null}}catch(s){gn={at:e,rows:null,error:String(s?.message||s)}}return gn.rows}var iu=()=>({enabled:pg(),lastPollAt:gn.at||null,error:gn.error}),su=new Map;async function I_(t,e){let n=`${t.id}:${e.name}`;if(!su.has(n)){let r=await D(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);su.set(n,o)}return su.get(n)}async function hg(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!pg())return;let r=await __({fetchImpl:n});if(!r||!r.length)return;let o=await Ch(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await I_(t,e),a=mg(),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=le(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}})}A_({sinceMs:c,seen:[...i].slice(-E_)})}catch{}}import{execFile as M_}from"node:child_process";import{promisify as N_}from"node:util";import wg from"node:fs/promises";import gg from"node:path";import R_ from"node:os";var yg=process.env.BOSTRAT_STATE_DIR||gg.join(process.env.HOME||R_.tmpdir(),".bostrat"),$_=gg.join(yg,"github-latency.ndjson"),O_=100,Ka=[],cu=new Map;function Ga({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};Ka.push(l),Ka.length>O_&&Ka.shift();let u=cu.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)),cu.set(t,u),C_(l),l}async function C_(t){try{await wg.mkdir(yg,{recursive:!0}),await wg.appendFile($_,JSON.stringify(t)+`
147
+ `)}catch{}}function bg(){let t={};for(let[e,n]of cu)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:Ka.slice(-20)}}async function P_(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([Pr("git",["-C",t,"rev-parse","HEAD"]),Pr("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 Pr=N_(M_),B_={timeout:3e4,maxBuffer:8*1024*1024};async function Yt(t,e,n={}){let{caller:r="prs",...o}=n,s=await ae({cwd:t});je(r);try{let{stdout:a}=await Pr("gh",e,{cwd:t,...B_,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw dn(a)&&fn(a),a}}var Va=null;async function du(){if(Va&&Date.now()-Va.at<6e4)return Va.data;let t;try{je("auth");let e=await Pr("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 Va={at:Date.now(),data:t},t}async function D_(t){try{let{stdout:e}=await Pr("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function xg(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 lu(t){let e=(t||[]).map(xg),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 fu="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Sg=fu.replace("statusCheckRollup,","");function pu(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function vg(t,e){if(!e)return null;let n=await tt(t);if(!n)return null;try{let r=await Yt(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 Tg(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 Ag(t,e){let n=await vg(t,e);return n===null?{...lu([]),unavailable:!0}:lu(Tg(n))}function za(t,e=lu(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 j_=9e4,Nr=new Map;function L_(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Nr.delete(s);continue}r.add(s),Nr.has(s)||Nr.set(s,n),o.mergeabilityPending=n-Nr.get(s)<j_}for(let o of Nr.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Nr.delete(o);return e}async function Br(t,e){let n=await Le(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Yt(n.path,["pr","view",String(r),"--json",fu]);return za(JSON.parse(o))}catch(o){if(!pu(o))throw o;let s=await Yt(n.path,["pr","view",String(r),"--json",Sg]),a=JSON.parse(s);return za(a,await Ag(n.path,a.headRefOid))}}var kg=(t,e)=>t.number-e.number;async function Xo(t,{caller:e="prs"}={}){try{let n=await Yt(t,["pr","list","--json",fu,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>za(r)).sort(kg)}catch(n){if(!pu(n))throw n;let r=await Yt(t,["pr","list","--json",Sg,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>za(o,await Ag(t,o.headRefOid))))).sort(kg)}}var F_=3e4,U_=1500,Jo=new Map;function ke(){Jo.clear()}async function H_(t,e){if(!t)return e;for(let n of e){let r=await $t(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 Sa(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ta(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function Eg(){let t=await du();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await ve())await D_(a.path)&&e.push(a);let n=Date.now(),r=La(n)>0,o=await Promise.all(e.map(async a=>{let i=Jo.get(a.path),c=i?.pending?U_:F_;if(i&&(r||n-i.at<c))return i.entry;try{let l=await tt(a.path).catch(()=>null),u=L_(a.path,await Xo(a.path,{caller:"tab"}),n);if(await H_(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(g=>g.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Ga({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 Jo.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:be(l)}}})),s=new Set(e.map(a=>a.path));for(let a of Jo.keys())s.has(a)||Jo.delete(a);return{auth:t,repos:o}}async function Le(t){let e=(await ve()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function zn(t){return oo(t)}var Yo=new Set(["squash","merge","rebase"]);async function Dr(t,e,{method:n="squash"}={}){let r=await Le(t);if(!Yo.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 ae({cwd:r.path})).GH_TOKEN){let a=await Di().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Yt(r.path,s)}catch(a){throw new Error(be(a))}finally{ke()}return{ok:!0,number:o}}async function Zo(t,e){let n=await Le(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Yt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(be(s))}finally{ke()}let o=await tt(n.path).catch(()=>null);return o&&await nh(o,r).catch(()=>{}),{ok:!0,number:r}}async function mu(t,e){let n=await Le(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Yt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(be(o))}finally{ke()}return{ok:!0,number:r}}var W_=2500,q_=9e4,K_=t=>new Promise(e=>setTimeout(e,t));function hu(t){return t.green&&!t.isDraft}function G_(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 _g(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:hu(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Ig(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var uu=new Map;function Rg(){let t={};for(let[e,n]of uu)t[e]={...n};return t}async function $g(t,{method:e="merge",onMerged:n=null}={}){let r=await Le(t);if(!Yo.has(e))throw new Error(`invalid merge method: ${e}`);if(uu.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Xo(r.path)).filter(hu).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 uu.set(r.path,s),z_(s,o,{listPrs:()=>Xo(r.path),merge:a=>Dr(r.path,a,{method:e}),sleep:K_,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function V_(t,e){let n=Date.now()+(e.settleMaxMs??q_),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(W_)}}async function z_(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 V_(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: ${be(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(!hu(a)){t.skipped.push({number:a.number,title:a.title,reason:G_(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:be(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),ke()}}async function wu(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Pr("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${be(l)}`)}await P_(t);let o=n||"";if((await ae({cwd:t})).GH_TOKEN){let l=await Di().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 Yt(t,s)}catch(l){throw new Error(be(l))}finally{ke()}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 Og(t,e){let n=await Le(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Yt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(be(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!pu(i))throw i;let c=await Yt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await vg(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(be(i));o=Tg(l).map(xg).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=J_(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Yt(n.path,["run","view",c,"--log-failed"]).then(u=>X_(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function J_(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function X_(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 ke(t){return String(t?.stderr||t?.message||t||"").trim().split(`
154
- `).find(n=>n.trim())||"gh failed"}var B_=6e3,Ya=new Map;function pu(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 xg(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await L(t)}catch{return}if(pu(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Ha(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Ya.get(t.id)||0)<B_)return;Ya.set(t.id,o);let s;try{s=await Zo(t.cwd,{caller:"reconcile"})}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await tt(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Ht({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 Do(i,a.number))){Ya.delete(t.id);return}}await $(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e,...i?{repo:i}:{}}}).catch(()=>{}),Ya.delete(t.id)}import D_ from"node:fs/promises";import Eg from"node:path";import j_ from"node:os";import{randomUUID as _g}from"node:crypto";var L_=process.env.BOSTRAT_STATE_DIR||Eg.join(process.env.HOME||j_.tmpdir(),".bostrat"),Ig=Eg.join(L_,"flows.json"),Sg=20,vg=80,Tg=500,Ag=80,Ue=null,mu=null;async function He(){return Ue||(mu||=(async()=>{let t=!0;try{Ue=JSON.parse(await D_.readFile(Ig,"utf8"))}catch{Ue={flows:[]},t=!1}finally{mu=null}if(Ue.flows||=[],!t&&Tt()){let{records:e,source:n}=await Zt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&Ue.flows.push(r.data);Ue.flows.length&&await jr().catch(()=>{})}}return Ue})(),mu)}async function jr(){await Ct(Ig,Ue)}var Za=t=>de("flows",{...t},{trackId:t.trackId}).catch(()=>{});function hu(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Rg({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>Sg)throw new Error(`steps must be an array of 1-${Sg}`);let r=await On({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>Tg)throw new Error(`${l}: caption over ${Tg} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Ag)throw new Error(`${l}: title over ${Ag} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let m=r.filter(S=>S.id===d);if(m.length||(m=r.filter(S=>S.name.toLowerCase()===d.toLowerCase())),!m.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(m.length>1)throw new Error(`${l}: "${d}" is ambiguous (${m.map(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let h=m[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(S=>S.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let g=o.findIndex(S=>S.mockId===h.id),b=g>=0?o.splice(g,1)[0].id:_g().slice(0,8);s.push({id:b,mockId:h.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 F_(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>vg)throw new Error(`name over ${vg} chars`);return t.trim()}async function $g({trackId:t}={}){let e=await He();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function U_(t){let n=(await He()).flows.find(r=>r.id===t);return n?{...n}:null}async function Og(t){let e=await U_(t);if(!e)return null;let n=new Map((await On({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=Ie(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 Mg({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=F_(e),i=await He(),c=i.flows.find(m=>m.trackId===t&&m.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 Rg({trackId:t,steps:n}),f=Date.now(),d={id:_g().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 jr(),await hu(d,"created"),Za(d),{...d}}async function Cg(t,{steps:e,note:n}={}){let o=(await He()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Rg({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 jr(),await hu(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Za(o),{...o}}async function Ng(t){let e=await He(),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 jr(),await hu(r,"deleted"),Yt("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Pg(t){return(await He()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function Bg(t){let n=(await He()).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(),Za(r);return n.length&&await jr(),n.length}async function Dg(t){let e=await He(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await jr();for(let r of n)Yt("flows",r.id).catch(()=>{})}return n.length}async function jg(){if(!Tt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Zt("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await He();for(let r of n.flows)await Za(r);return{pushed:n.flows.length,source:"plane"}}import wu from"node:fs/promises";import es from"node:path";import{randomUUID as H_}from"node:crypto";var Fg=".bostrat/uploads",gu=t=>es.join(t,Fg),Lg=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,W_=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),q_=new Set(["pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","rtf","txt","md","markdown","rst","csv","tsv","json","jsonl","yaml","yml","xml","html","htm","log","ini","toml","conf","env","js","jsx","ts","tsx","py","rb","go","rs","java","kt","c","h","cpp","cc","cs","php","swift","sh","bash","zsh","sql","css","scss"]);function yu(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return W_.has(e)?"image":q_.has(e)?"doc":null}function K_(t){let r=es.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 Ug(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>Lg)throw new Error(`file too large \u2014 max ${Math.floor(Lg/(1024*1024))} MB`);let r=K_(e),o=es.extname(r).slice(1),s=yu(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=H_().slice(0,8),i=gu(t);await wu.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await wu.writeFile(es.join(i,c),n,{mode:384}),await wu.chmod(es.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${Fg}/${c}`,kind:s,size:n.length}}function Hg(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var G_=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function bu(t){return t.role==="user"&&G_.test((t.text||"").trim())}function Lr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var J_=V_(z_),We=process.env.BOSTRAT_STATE_DIR||H.join(process.env.HOME||"/tmp",".bostrat"),Yg=H.join(We,"tracks.json"),X_=H.join(We,"events.jsonl"),Eu=H.join(We,"workspaces"),Y_=process.env.BOSTRAT_DEFAULT_REPO||null,ei={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"}},ni=Object.keys(ei);function Z_(t,{skipPermissions:e=!0}={}){let n=ei[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 Zg(t,e,{skipPermissions:n=!0}={}){let r=ei[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 ls(t){let e=ei[t];if(e)return process.env[e.envVar]||e.base}var Vn={at:0,value:null};async function Iu({maxAgeMs:t=6e4}={}){if(Vn.value&&Date.now()-Vn.at<t)return Vn.value;let e=await Promise.all(ni.map(async n=>[n,await js(ls(n))]));return Vn.value=Object.fromEntries(e),Vn.at=Date.now(),Vn.value}async function ri(t,e=!0){let n=Z_(t,{skipPermissions:e});if(!await js(n)){Vn.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.":Yi[r]?`Install it on this node: ${Yi[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 Q_(t,e=oi){return`${t.trim()}
153
+ `+r.slice(-n)),r}function be(t){return String(t?.stderr||t?.message||t||"").trim().split(`
154
+ `).find(n=>n.trim())||"gh failed"}var Y_=6e3,Ja=new Map;function gu(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 Cg(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await D(t)}catch{return}if(gu(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Fa(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Ja.get(t.id)||0)<Y_)return;Ja.set(t.id,o);let s;try{s=await Xo(t.cwd,{caller:"reconcile"})}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await tt(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Wt({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 Po(i,a.number))){Ja.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(()=>{}),Ja.delete(t.id)}import Z_ from"node:fs/promises";import Dg from"node:path";import Q_ from"node:os";import{randomUUID as jg}from"node:crypto";var tI=process.env.BOSTRAT_STATE_DIR||Dg.join(process.env.HOME||Q_.tmpdir(),".bostrat"),Lg=Dg.join(tI,"flows.json"),Mg=20,Ng=80,Pg=500,Bg=80,Fe=null,yu=null;async function Ue(){return Fe||(yu||=(async()=>{let t=!0;try{Fe=JSON.parse(await Z_.readFile(Lg,"utf8"))}catch{Fe={flows:[]},t=!1}finally{yu=null}if(Fe.flows||=[],!t&&ft()){let{records:e,source:n}=await vt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&Fe.flows.push(r.data);Fe.flows.length&&await jr().catch(()=>{})}}return Fe})(),yu)}async function jr(){await Mt(Lg,Fe)}var Xa=t=>Rt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function bu(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 Fg({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>Mg)throw new Error(`steps must be an array of 1-${Mg}`);let r=await Cn({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>Pg)throw new Error(`${l}: caption over ${Pg} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Bg)throw new Error(`${l}: title over ${Bg} 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],g=null;if(c.v!=null&&(g=Number(c.v),!Number.isInteger(g)||!(m.versions||[]).some(S=>S.v===g)))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:jg().slice(0,8);s.push({id:b,mockId:m.id,caption:u,title:f,v:g})}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 eI(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Ng)throw new Error(`name over ${Ng} chars`);return t.trim()}async function Ug({trackId:t}={}){let e=await Ue();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function nI(t){let n=(await Ue()).flows.find(r=>r.id===t);return n?{...n}:null}async function Hg(t){let e=await nI(t);if(!e)return null;let n=new Map((await Cn({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=_e(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 Wg({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=eI(e),i=await Ue(),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 Fg({trackId:t,steps:n}),f=Date.now(),d={id:jg().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 jr(),await bu(d,"created"),Xa(d),{...d}}async function qg(t,{steps:e,note:n}={}){let o=(await Ue()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Fg({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 jr(),await bu(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Xa(o),{...o}}async function Kg(t){let e=await Ue(),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 jr(),await bu(r,"deleted"),Qt("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Gg(t){return(await Ue()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function Vg(t){let n=(await Ue()).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(),Xa(r);return n.length&&await jr(),n.length}async function zg(t){let e=await Ue(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await jr();for(let r of n)Qt("flows",r.id).catch(()=>{})}return n.length}async function Jg(){if(!ft())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await vt("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Ue();for(let r of n.flows)await Xa(r);return{pushed:n.flows.length,source:"plane"}}import ku from"node:fs/promises";import Qo from"node:path";import{randomUUID as rI}from"node:crypto";var Yg=".bostrat/uploads",xu=t=>Qo.join(t,Yg),Xg=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,oI=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),sI=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 Su(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return oI.has(e)?"image":sI.has(e)?"doc":null}function aI(t){let r=Qo.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 Zg(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>Xg)throw new Error(`file too large \u2014 max ${Math.floor(Xg/(1024*1024))} MB`);let r=aI(e),o=Qo.extname(r).slice(1),s=Su(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=rI().slice(0,8),i=xu(t);await ku.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await ku.writeFile(Qo.join(i,c),n,{mode:384}),await ku.chmod(Qo.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${Yg}/${c}`,kind:s,size:n.length}}function Qg(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var iI=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function vu(t){return t.role==="user"&&iI.test((t.text||"").trim())}function Lr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var uI=lI(cI),He=process.env.BOSTRAT_STATE_DIR||F.join(process.env.HOME||"/tmp",".bostrat"),cy=F.join(He,"tracks.json"),dI=F.join(He,"events.jsonl"),$u=F.join(He,"workspaces"),fI=process.env.BOSTRAT_DEFAULT_REPO||null,Qa={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"}},ti=Object.keys(Qa);function pI(t,{skipPermissions:e=!0}={}){let n=Qa[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 ly(t,e,{skipPermissions:n=!0}={}){let r=Qa[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=Qa[t];if(e)return process.env[e.envVar]||e.base}var Jn={at:0,value:null};async function Cu({maxAgeMs:t=6e4}={}){if(Jn.value&&Date.now()-Jn.at<t)return Jn.value;let e=await Promise.all(ti.map(async n=>[n,await Ls(is(n))]));return Jn.value=Object.fromEntries(e),Jn.at=Date.now(),Jn.value}async function ei(t,e=!0){let n=pI(t,{skipPermissions:e});if(!await Ls(n)){Jn.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.":nc[r]?`Install it on this node: ${nc[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 ni=".bostrat/handoff/latest.md";function mI(t,e=ni){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 tI=/\(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 Wg(t){return t.role==="user"&&tI.test((t.text||"").trim())}var Qa=".bostrat/handoff/subtask-result.md",rs=".bostrat/handoff/lead-context.md",eI=(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 ${rs} \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 hI=/\(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 ty(t){return t.role==="user"&&hI.test((t.text||"").trim())}var Ya=".bostrat/handoff/subtask-result.md",es=".bostrat/handoff/lead-context.md",wI=(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 ${es} \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 ${Qa} (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 ${rs} \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 ${Ya} (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 ${es} \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 ${Qa} 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.`,nI=(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."),Se=null,ku=null;async function q(){return Se||(ku||=(async()=>{try{Se=JSON.parse(await U.readFile(Yg,"utf8"))}catch{Se={tracks:[]}}finally{ku=null}return Se})(),ku)}async function V(){await Ct(Yg,Se)}var nt=(t,e)=>t.tracks.find(n=>n.id===e)||null,xu=Promise.resolve();function rI(t,e){let n=JSON.stringify({trackId:t,...e})+`
165
- `;return xu=xu.then(()=>U.mkdir(We,{recursive:!0})).then(()=>U.appendFile(X_,n)).catch(()=>{}),xu}var ct=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),rI(t.id,r),mw(t,r),r};wf(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await q()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(ct(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await V().catch(()=>{}))});var Su=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),oI=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function Qg(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=>oI.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 ty(t,e,n,{kind:r}={}){let o=await Y(e);if(!o)throw new Error(`box "${e}" is not running`);let s=ls(o.agent);await Qe({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await vt(e,n,{..._e(o.agent),kind:r||"orchestration"}),await G({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await q(),i=nt(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);ct(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await V()}var Jn=new Set(["merged","conflict","canceled","failed","handed-off"]),qg=new Map;async function sI(t){let e=H.join(Ys(t),`${t.id}.ndjson`),n;try{n=await U.stat(e)}catch{return null}let r=qg.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 qg.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function aI(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Gl(n.name,n.agent)}async function gn(t,e=null){let n=e??new Map((await En()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=Jn.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([sI(t),aI(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a}}var iI=5e3;function cI(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-iI,d=r[u+1]?.createdAt??1/0,m=o.find(h=>!s.has(h.id)&&h.birthMs>=f&&h.birthMs<d);m&&(s.add(m.id),c.push({box:l.name,sessionId:m.id}))}return c}async function ey(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=uf(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=cI(u,a,d=>i.has(d));for(let d of f){let m=u.find(h=>h.name===d.box);m&&(m.sessionId=d.sessionId),await Qd(d.box,d.sessionId),n=!0}}return n}async function ot(){let t=await q();await ey(t)&&await V();let e=new Map((await En()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>gn(n,e)))}async function ny(t,e){let n=await q();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 ry(t){let e=oo(t);if(!e)return null;let n=await q();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&xd(o.hookTokenHash,e))return{track:r,box:o};return null}async function Q(t){let e=await q(),n=nt(e,t);return n?(await ey(e)&&await V(),gn(n)):null}var lI={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function Kg(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(lI[t.agent]||[]).some(n=>n.test(e))}var uI=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function Gg(t){let e=(t.text||"").trim();return e?uI.some(n=>n.test(e)):!1}var dI=10*6e4;async function Ru(t,e={}){let n=await Q(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=us(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=_c(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Gp(n),...l],d={windows:[...Zf(n),...Qf(n,ta),...a,...u]},h=(await Promise.all(n.boxes.map(k=>xl(k,e)))).flat(),w=h.map(k=>{let K={kind:"message",...k};if(K.role==="assistant"&&(K.text||"").trim()&&K.tools?.length){let Zn=qo(K.agent).cliTools,As=K.tools.filter(Qn=>!Zn.has(Qn));return{...K,tools:As.length?As:void 0}}return K}).map(k=>{if(k.role!=="user")return k;let K=qa(k.text);return K?K.kind==="suppress"?null:{...k,note:K,text:Yw(K)}:k}).filter(Boolean).filter(k=>!(k.role==="user"&&r.has((k.text||"").trim()))).filter(k=>!Kg(k)).filter(k=>!Gg(k)).filter(k=>!Wg(k)).filter(k=>!bu(k)).filter(k=>k.role!=="assistant"||!!(k.text||"").trim()||!!k.images?.length).filter(k=>!tp(k,d)),g=w.filter(k=>k.failed&&k.text===hl);if(g.length){let k=new Map;for(let K of g){if(!k.has(K.box)){let Qn=[...(await gt(K.box,{lines:At}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Ri=>Ri[1]);k.set(K.box,Qn.length?Qn[Qn.length-1]:null)}let Zn=k.get(K.box);Zn&&(K.text=`\u25A0 ${Zn}`)}}let b=await Promise.all(n.boxes.map(k=>Oh(k,e)));await Jw(n,b.flat()),await zw(n,w),await hw(n),await Qp(n,w).catch(()=>{});for(let k of n.boxes)k.agent==="codex"&&k.state==="live"&&await tg(n,k),k.agent==="cursor"&&k.state==="live"&&await og(n,k);let S=await Promise.all(n.boxes.map(k=>$h(k,e)));await Vw(n,S.flat().filter(k=>!Su(k.box,k.ts,u)));let A=await L(n),C=c&&i.viewId?i.viewId:null,z=Aw(A),O=Xo(A).filter(k=>!(k.kind==="message"&&(Kg(k)||Gg(k)||Wg(k)||bu(k)))&&!(k.kind==="view-card"&&C&&k.viewId===C)),F=Ew(O,z),T=O.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&!F.has(k)&&Su(k.box,k.ts,z))),M=Ql(A,T).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=>!Su(k.anchorBox,k.anchorTs??k.ts,u)),W=[...T,...M].filter(k=>k.ts!=null).sort((k,K)=>k.ts-K.ts),st=fI(n),y=st.size?W.filter(k=>k.kind==="event"||!st.has(k.box??k.anchorBox)):W,p=n.boxes.find(k=>k.name===n.leadBox)||null,v=p?await go(p.cwd,p.sessionId,{fallbackNewest:!1}):null;if(!v&&p){let k=[...A].reverse().find(K=>K.type==="context.usage"&&K.boxId===p.name);k?.payload?.window&&(v={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 E=[...h].reverse().find(k=>k.role==="assistant"&&k.model),rt=E?.model||null,I=[...n.events||[]].reverse().find(k=>k.type==="model-change"),R=v?.model||rt;I&&(!E?.ts||I.at>=E.ts)&&(R=I.to);let _=p?await gt(p.name,{lines:At}).catch(()=>""):"",P=p&&Rr(p.agent,_)?await Xe(p.name).catch(()=>_):_,et=p?Kn(p,P):null;!et&&p&&!Ze(p.agent,_)&&!zg(n.id).length&&(et=Ml(y,{leadBoxId:p.name,provider:p.agent,isAnswered:k=>A.some(K=>K.type==="prompt.answered"&&K.payload?.promptId===k)})),et&&ww(n,p,et);let wt=y.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&k.ask&&!String(k.text||"").trim()&&!k.images?.length)),bt=!!p&&!et&&Ze(p.agent,_);p&&!p.readyAt&&(bt||et||_&&Gs(p.agent,_))&&(p.readyAt=Date.now(),await V());let $t=!!p&&!p.readyAt&&Date.now()-(n.createdAt||0)<dI;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&p&&Gs(p.agent,_)&&!et){let k=await q(),K=nt(k,n.id);K?.setup&&!K.setup.connectedAt&&(K.setup.connectedAt=Date.now(),n.setup.connectedAt=K.setup.connectedAt,await $(K,{type:"provider.connected",actor:"system",source:"pane",boxId:p.name,provider:p.agent,payload:{provider:p.agent,ts:K.setup.connectedAt}}).catch(()=>{}),await V())}let Kt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Xt=null;if(c){let k=Date.now()-i.startedAt;if(i.viewId&&!bt){let K=la(n.id);K&&(ct(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:K.finishedAt}),await V())}else if(et||k>qp&&!bt&&!i.viewId||k>Ec){let K=et?"needs-input":k>Ec?"timeout":"no-view";la(n.id,{error:K})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:p?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:K}}).catch(()=>{})}else Xt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:wt,context:v?{pct:v.pct,occupancy:v.occupancy,window:v.window,windowKnown:v.windowKnown,model:v.model}:null,models:{current:R,options:lw(p?.agent)},pendingPrompt:et,working:bt,leadStarting:$t,handoff:Kt,view:Xt,queued:zg(n.id).filter(k=>k.kind!=="ship-notify"&&k.kind!=="prompt-answer")}}function fI(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 oy(t,e){let n=await Q(t);if(!n)return null;let r=Kr(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=Xo(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Ql(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 gt(o,{lines:At}).catch(()=>"");u=Ze(l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function $u(t,{promptId:e,answers:n,text:r}={}){let o=nt(await q(),t);if(!o)throw new Error(`track "${t}" not found`);let s=o.boxes.find(d=>d.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await Y(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Il(s);if(i||(i=await pI(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 vt(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 xt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=Jh(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await oe(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=Hh(i,n||[]);await xt(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=vf(i,n||[]);await vt(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 m=0;m<d.length;m++){let h=d[m],w=(Array.isArray(n?.[m])?n[m]:[]).map(g=>h.options?.[g]).filter(Boolean).map(g=>({label:g.label,description:g.description||""}));ql(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:m,questionTotal:i.questionTotal??d.length,header:h.header||"",question:h.question||"",multiSelect:!!h.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(()=>{}),ql(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function pI(t,e){if(!e)return null;let n=await L(t).catch(()=>[]);return Ml(Xo(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function sy(t,{promptId:e,questionIndex:n}={}){let r=nt(await q(),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 Y(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Il(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=Wh(a,n);return c.length?(await xt(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 mI=1500,os=new Map;function Ou(t){let e=os.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},os.set(t,e)),e}function zg(t){return(os.get(t)?.items??[]).map(e=>({...e}))}function si(t){let e=os.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}function ay(t){let e=Ou(t);e.timer||(e.timer=setInterval(()=>hI(t).catch(()=>{}),mI))}async function hI(t){let e=os.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=nt(await q(),t);if(!n||n.archivedAt){si(t);return}let r=us(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Lr(e,!1);return}let o=await Y(n.leadBox);if(!o){Lr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await gt(n.leadBox,{lines:At}).catch(()=>"");if(Ks(s?.agent,a)){Lr(e,!1);return}if(Rr(s?.agent,a)){let c=await Xe(n.leadBox).catch(()=>a);if(Kn(s,c)){Lr(e,!1);return}}if(!Lr(e,!0))return;let i=e.items.shift();e.idleSince=null,await iy(n,s,o,i.text,i.kind??null,i.attachments??null),await V()}finally{e.flushing=!1}}}async function iy(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=Q_(r,t.pendingHandoffHydrate.artifact||oi),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(u=>u&&u.rel):[];if(c.length){let u=Hg(c.map(f=>f.rel));a=a.trim()?`${a.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 ${Ya} 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.`,gI=(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."),xe=null,Tu=null;async function W(){return xe||(Tu||=(async()=>{try{xe=JSON.parse(await L.readFile(cy,"utf8"))}catch{xe={tracks:[]}}finally{Tu=null}return xe})(),Tu)}async function J(){await Mt(cy,xe)}var et=(t,e)=>t.tracks.find(n=>n.id===e)||null,Au=Promise.resolve();function yI(t,e){let n=JSON.stringify({trackId:t,...e})+`
165
+ `;return Au=Au.then(()=>L.mkdir(He,{recursive:!0})).then(()=>L.appendFile(dI,n)).catch(()=>{}),Au}var it=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),yI(t.id,r),Aw(t,r),r};Nf(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await W()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(it(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await J().catch(()=>{}))});var Eu=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),bI=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function uy(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=>bI.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 dy(t,e,n,{kind:r}={}){let o=await Z(e);if(!o)throw new Error(`box "${e}" is not running`);let s=is(o.agent);await Qe({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await xt(e,n,{...Ee(o.agent),kind:r||"orchestration"}),await V({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await W(),i=et(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);it(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await J()}var Xn=new Set(["merged","conflict","canceled","failed","handed-off"]),ey=new Map;async function kI(t){let e=F.join(Zs(t),`${t.id}.ndjson`),n;try{n=await L.stat(e)}catch{return null}let r=ey.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await D(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return ey.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function xI(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Xl(n.name,n.agent)}async function yn(t,e=null){let n=e??new Map((await _n()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=Xn.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([kI(t),xI(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a}}var SI=5e3;function vI(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-SI,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 fy(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=If(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=vI(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 yf(d.box,d.sessionId),n=!0}}return n}async function rt(){let t=await W();await fy(t)&&await J();let e=new Map((await _n()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>yn(n,e)))}async function py(t,e){let n=await W();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function my(t){let e=no(t);if(!e)return null;let n=await W();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Id(o.hookTokenHash,e))return{track:r,box:o};return null}async function Q(t){let e=await W(),n=et(e,t);return n?(await fy(e)&&await J(),yn(n)):null}var TI={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function ny(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(TI[t.agent]||[]).some(n=>n.test(e))}var AI=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function ry(t){let e=(t.text||"").trim();return e?AI.some(n=>n.test(e)):!1}var EI=10*6e4;async function Mu(t,e={}){let n=await Q(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=cs(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=Rc(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...em(n),...l],d={windows:[...wp(n),...gp(n,ea),...a,...u]},m=(await Promise.all(n.boxes.map(k=>Al(k,e)))).flat(),g=m.map(k=>{let G={kind:"message",...k};if(G.role==="assistant"&&(G.text||"").trim()&&G.tools?.length){let Qn=Ho(G.agent).cliTools,vs=G.tools.filter(tr=>!Qn.has(tr));return{...G,tools:vs.length?vs:void 0}}return G}).map(k=>{if(k.role!=="user")return k;let G=Ha(k.text);return G?G.kind==="suppress"?null:{...k,note:G,text:cg(G)}:k}).filter(Boolean).filter(k=>!(k.role==="user"&&r.has((k.text||"").trim()))).filter(k=>!ny(k)).filter(k=>!ry(k)).filter(k=>!ty(k)).filter(k=>!vu(k)).filter(k=>k.role!=="assistant"||!!(k.text||"").trim()||!!k.images?.length).filter(k=>!yp(k,d)),y=g.filter(k=>k.failed&&k.text===bl);if(y.length){let k=new Map;for(let G of y){if(!k.has(G.box)){let tr=[...(await ht(G.box,{lines:Tt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map($i=>$i[1]);k.set(G.box,tr.length?tr[tr.length-1]:null)}let Qn=k.get(G.box);Qn&&(G.text=`\u25A0 ${Qn}`)}}let b=await Promise.all(n.boxes.map(k=>Hh(k,e)));await ag(n,b.flat()),await og(n,g),await Ew(n),await cm(n,g).catch(()=>{});for(let k of n.boxes)k.agent==="codex"&&k.state==="live"&&await dg(n,k),k.agent==="cursor"&&k.state==="live"&&await hg(n,k);let S=await Promise.all(n.boxes.map(k=>Uh(k,e)));await sg(n,S.flat().filter(k=>!Eu(k.box,k.ts,u)));let E=await D(n),M=c&&i.viewId?i.viewId:null,z=Bw(E),$=zo(E).filter(k=>!(k.kind==="message"&&(ny(k)||ry(k)||ty(k)||vu(k)))&&!(k.kind==="view-card"&&M&&k.viewId===M)),j=Dw($,z),v=$.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&!j.has(k)&&Eu(k.box,k.ts,z))),C=ru(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=>!Eu(k.anchorBox,k.anchorTs??k.ts,u)),H=[...v,...C].filter(k=>k.ts!=null).sort((k,G)=>k.ts-G.ts),nt=_I(n),ot=nt.size?H.filter(k=>k.kind==="event"||!nt.has(k.box??k.anchorBox)):H,w=n.boxes.find(k=>k.name===n.leadBox)||null,h=w?await yo(w.cwd,w.sessionId,{fallbackNewest:!1}):null;if(!h&&w){let k=[...E].reverse().find(G=>G.type==="context.usage"&&G.boxId===w.name);k?.payload?.window&&(h={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 T=[...m].reverse().find(k=>k.role==="assistant"&&k.model),_=T?.model||null,O=[...n.events||[]].reverse().find(k=>k.type==="model-change"),I=h?.model||_;O&&(!T?.ts||O.at>=T.ts)&&(I=O.to);let A=w?await ht(w.name,{lines:Tt}).catch(()=>""):"",K=w&&Rr(w.agent,A)?await Xe(w.name).catch(()=>A):A,q=w?Gn(w,K):null;!q&&w&&!Ze(w.agent,A)&&!oy(n.id).length&&(q=Bl(ot,{leadBoxId:w.name,provider:w.agent,isAnswered:k=>E.some(G=>G.type==="prompt.answered"&&G.payload?.promptId===k)})),q&&_w(n,w,q);let dt=ot.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&k.ask&&!String(k.text||"").trim()&&!k.images?.length)),yt=!!w&&!q&&Ze(w.agent,A);w&&!w.readyAt&&(yt||q||A&&Vs(w.agent,A))&&(w.readyAt=Date.now(),await J());let Ot=!!w&&!w.readyAt&&Date.now()-(n.createdAt||0)<EI;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&w&&Vs(w.agent,A)&&!q){let k=await W(),G=et(k,n.id);G?.setup&&!G.setup.connectedAt&&(G.setup.connectedAt=Date.now(),n.setup.connectedAt=G.setup.connectedAt,await R(G,{type:"provider.connected",actor:"system",source:"pane",boxId:w.name,provider:w.agent,payload:{provider:w.agent,ts:G.setup.connectedAt}}).catch(()=>{}),await J())}let Vt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Zt=null;if(c){let k=Date.now()-i.startedAt;if(i.viewId&&!yt){let G=ca(n.id);G&&(it(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:G.finishedAt}),await J())}else if(q||k>Qp&&!yt&&!i.viewId||k>Ic){let G=q?"needs-input":k>Ic?"timeout":"no-view";ca(n.id,{error:G})&&await R(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:w?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:G}}).catch(()=>{})}else Zt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:dt,context:h?{pct:h.pct,occupancy:h.occupancy,window:h.window,windowKnown:h.windowKnown,model:h.model}:null,models:{current:I,options:kw(w?.agent)},pendingPrompt:q,working:yt,leadStarting:Ot,handoff:Vt,view:Zt,queued:oy(n.id).filter(k=>k.kind!=="ship-notify"&&k.kind!=="prompt-answer")}}function _I(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 hy(t,e){let n=await Q(t);if(!n)return null;let r=Kr(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 D(n),a=zo(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=ru(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 ht(o,{lines:Tt}).catch(()=>"");u=Ze(l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Nu(t,{promptId:e,answers:n,text:r}={}){let o=et(await W(),t);if(!o)throw new Error(`track "${t}" not found`);let s=o.boxes.find(d=>d.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await Z(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Cl(s);if(i||(i=await II(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 xt(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 bt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=aw(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await oe(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=Qh(i,n||[]);await bt(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=Uf(i,n||[]);await xt(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],g=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));zl(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:g})}return await V({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 V({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),zl(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function II(t,e){if(!e)return null;let n=await D(t).catch(()=>[]);return Bl(zo(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function wy(t,{promptId:e,questionIndex:n}={}){let r=et(await W(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await Z(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Cl(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=tw(a,n);return c.length?(await bt(r.leadBox,c,{kind:"navigate-prompt"}),await V({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 RI=1500,ns=new Map;function Pu(t){let e=ns.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},ns.set(t,e)),e}function oy(t){return(ns.get(t)?.items??[]).map(e=>({...e}))}function ri(t){let e=ns.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}function gy(t){let e=Pu(t);e.timer||(e.timer=setInterval(()=>$I(t).catch(()=>{}),RI))}async function $I(t){let e=ns.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=et(await W(),t);if(!n||n.archivedAt){ri(t);return}let r=cs(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Lr(e,!1);return}let o=await Z(n.leadBox);if(!o){Lr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await ht(n.leadBox,{lines:Tt}).catch(()=>"");if(Gs(s?.agent,a)){Lr(e,!1);return}if(Rr(s?.agent,a)){let c=await Xe(n.leadBox).catch(()=>a);if(Gn(s,c)){Lr(e,!1);return}}if(!Lr(e,!0))return;let i=e.items.shift();e.idleSince=null,await yy(n,s,o,i.text,i.kind??null,i.attachments??null),await J()}finally{e.flushing=!1}}}async function yy(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=mI(r,t.pendingHandoffHydrate.artifact||ni),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(u=>u&&u.rel):[];if(c.length){let u=Qg(c.map(f=>f.rel));a=a.trim()?`${a.trim()}
166
166
 
167
- ${u}`:u}let l=ct(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...c.length?{attachments:c}:{}});delete t.draft;try{await Qe({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:ls(n.agent)}),await vt(t.leadBox,a,{..._e(e?.agent),kind:i?"lead-handoff-first-prompt":"say"})}catch(u){throw ct(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:l.at,text:r}),u}await EI(t,e,a)||(await uy(t.leadBox,e?.agent,a.length+8).catch(()=>{}),ct(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:l.at,text:r,reason:"unsubmitted"}),o||(t.draft={text:r,updatedAt:Date.now()})),i&&await G({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function cy(t,{actions:e}={}){Ou(t).items.push({id:cs(),text:_w(e),ts:Date.now(),kind:"ship-notify"}),ay(t)}async function oe(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"||!$r(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=nt(await q(),t);if(!s)throw new Error(`track "${t}" not found`);if(s.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before driving it`);let a=await Y(s.leadBox);if(!a&&await Pu(s)&&(await V(),a=await Y(s.leadBox)),!a)throw new Error(`lead box "${s.leadBox}" is not running`);let i=s.boxes.find(d=>d.name===s.leadBox),c=$r(e),l=Ou(t),u=await gt(s.leadBox,{lines:At}).catch(()=>""),f=l.items.length>0||Ks(i?.agent,u);if(!f&&Rr(i?.agent,u)){let d=await Xe(s.leadBox).catch(()=>u);f=!!Kn(i,d)}if(f){let d={id:cs(),text:c,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),ay(t),delete s.draft,await V(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await iy(s,i,a,c,n,o),await V(),{ok:!0,leadBox:s.leadBox}}var Vg="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 ly(t,{hint:e}={}){let n=nt(await q(),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=_c(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";Kp(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${Vg}
167
+ ${u}`:u}let l=it(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...c.length?{attachments:c}:{}});delete t.draft;try{await Qe({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:is(n.agent)}),await xt(t.leadBox,a,{...Ee(e?.agent),kind:i?"lead-handoff-first-prompt":"say"})}catch(u){throw it(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:l.at,text:r}),u}await UI(t,e,a)||(await xy(t.leadBox,e?.agent,a.length+8).catch(()=>{}),it(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:l.at,text:r,reason:"unsubmitted"}),o||(t.draft={text:r,updatedAt:Date.now()})),i&&await V({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function by(t,{actions:e}={}){Pu(t).items.push({id:as(),text:jw(e),ts:Date.now(),kind:"ship-notify"}),gy(t)}async function oe(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"||!$r(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=et(await W(),t);if(!s)throw new Error(`track "${t}" not found`);if(s.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before driving it`);let a=await Z(s.leadBox);if(!a&&await Lu(s)&&(await J(),a=await Z(s.leadBox)),!a)throw new Error(`lead box "${s.leadBox}" is not running`);let i=s.boxes.find(d=>d.name===s.leadBox),c=$r(e),l=Pu(t),u=await ht(s.leadBox,{lines:Tt}).catch(()=>""),f=l.items.length>0||Gs(i?.agent,u);if(!f&&Rr(i?.agent,u)){let d=await Xe(s.leadBox).catch(()=>u);f=!!Gn(i,d)}if(f){let d={id:as(),text:c,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),gy(t),delete s.draft,await J(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await yy(s,i,a,c,n,o),await J(),{ok:!0,leadBox:s.leadBox}}var sy="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 ky(t,{hint:e}={}){let n=et(await W(),t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it first`);let r=Rc(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";tm(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${sy}
168
168
 
169
- What to preview: ${String(e).trim()}`:Vg;try{await oe(t,a,{kind:"spin-up-view"})}catch(i){throw la(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Mu(t){let e=nt(await q(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);let n=e.boxes.find(f=>f.name===e.leadBox),r=si(t),o=await Y(e.leadBox),s=o?await yI(e.leadBox,n?.agent):{stopped:!1,sends:0},a=o&&s.stopped?await xI(e,n):null,i=!a&&o&&s.stopped?await TI(e,n):null,c=o?await bI(e).catch(()=>null):null,l=r.filter(f=>f.text&&!f.kind).map(f=>f.text),u=a||i;return l.length&&(u=[u,...l].filter(Boolean).join(`
169
+ What to preview: ${String(e).trim()}`:sy;try{await oe(t,a,{kind:"spin-up-view"})}catch(i){throw ca(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Bu(t){let e=et(await W(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);let n=e.boxes.find(f=>f.name===e.leadBox),r=ri(t),o=await Z(e.leadBox),s=o?await MI(e.leadBox,n?.agent):{stopped:!1,sends:0},a=o&&s.stopped?await BI(e,n):null,i=!a&&o&&s.stopped?await LI(e,n):null,c=o?await NI(e).catch(()=>null):null,l=r.filter(f=>f.text&&!f.kind).map(f=>f.text),u=a||i;return l.length&&(u=[u,...l].filter(Boolean).join(`
170
170
 
171
- `),e.draft={text:u,updatedAt:Date.now()},await V()),await G({flow:"interrupt",phase:"sent",trackId:t,sessionName:e.leadBox,agent:n?.agent,dropped:r.length,escSends:s.sends,stopped:s.stopped,unsent:!!a,lostRestored:!!i,queuedRestored:l.length,shipDismissed:!!c}).catch(()=>{}),{ok:!0,interrupted:!!o,stopped:s.stopped,dropped:r.length,restored:u||null}}var wI=[300,350,400],gI=3;async function yI(t,e){let n=0;for(let r=0;r<gI;r++){await xt(t,["Escape"],{kind:"interrupt"}),n++;for(let o of wI){await Gr(o);let s=await gt(t,{lines:At}).catch(()=>"");if(!Ze(e,s))return{stopped:!0,sends:n}}}return{stopped:!1,sends:n}}async function bI(t){let e=pu(await L(t));return e?.offerId?(await $(t,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,action:"dismiss",actions:null,reason:"interrupt"}}),e.offerId):null}var kI=[250,400,500,600];async function xI(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 kI){await Gr(r);let o=await gt(t.leadBox,{lines:At}).catch(()=>""),s=Ko(e.agent,o);if(s&&Rl(n.text,s))return await uy(t.leadBox,e.agent,n.text.length+8),t.draft={text:n.text,updatedAt:Date.now()},ct(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),await V(),n.text}return null}var SI=3e4,vI=[350,650,900];async function TI(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>SI||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!ne(r.text))return null;for(let s of vI){await Gr(s);let a;try{a=await xl(e,{})}catch{return null}if(Kh(a,r))return null;let i=await gt(t.leadBox,{lines:At}).catch(()=>"");if(Ko(e.agent,i))return null}return t.draft={text:r.text,updatedAt:Date.now()},ct(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:r.at,text:r.text,reason:"lost"}),await V(),r.text}var AI=[250,450,700],vu=2;async function EI(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=vu;r++){let o=!1;for(let s of AI){await Gr(s);let a=await gt(t.leadBox,{lines:At}).catch(()=>""),i=Gh(e.agent,n,a,{busy:Ze(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<vu&&(await xt(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:vu,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function uy(t,e,n){for(let r=0;r<3;r++){await Fs(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Gr(200);let o=await gt(t,{lines:At}).catch(()=>""),s=Ko(e,o);if(!s)return!0;n=s.length+8}return!1}async function dy(t,e){if(typeof e!="string")throw new Error("text (string) required");let n=nt(await q(),t);if(!n)throw new Error(`track "${t}" not found`);let r=$r(e);return r?n.draft={text:r,updatedAt:Date.now()}:delete n.draft,await V(),{ok:!0,draft:n.draft||null}}async function fy(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=nt(await q(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await Y(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=uw(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await go(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await Qe({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ls(o.agent)});let u=await dw(a,r.leadBox,i);return ct(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 py(t){for(let e of t){if(!e||!await Ca(e))continue;let n=await Zh(e).catch(()=>null);if(!n)continue;if((await U.stat(H.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Bt(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:H.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function Jo(t){let e=[];for(let n of Bt(t?.workspace)){if(!n?.dir)continue;let r=await Bl(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Dl(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function my(t){let e=t;typeof e=="string"&&!H.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await ao(e)??await ji(e));let n=await py([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 _I(t,e,n,r){let o=`track/${ju(e)}-${t}`,s=[];try{await U.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await my(i);if(s.some(m=>m.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await tt(c).catch(()=>null),u=H.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let m=2;a.has(u);m++)u=`${H.basename(c)}-${m}`;a.add(u);let f=H.join(n,u),d=await Na(c);await Or(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await ai(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Vt(i.base,i.dir).catch(()=>{});throw await U.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var hy=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 wy(t,e){let n=await q(),r=nt(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 my(typeof e=="string"?e.trim():e);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await tt(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=H.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${H.basename(s)}-${d}`;let l=H.join(o.dir,c),u=await Na(s);await Qh(s,o.branch)?await tw(s,l,o.branch):await Or(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 ai(o.dir,o.repos,o.branch),ct(r,"retagged",{action:"add",repo:a??s,name:c,workspace:hy(o)}),await V(),f}async function gy(t,e){let n=await q(),r=nt(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 re.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 Bl(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Dl(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 Vt(s.base,s.dir).catch(()=>{}),await U.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await ai(o.dir,o.repos,o.branch),ct(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:hy(o)}),await V(),{removed:s.name}}async function II(t,e,{repo:n,repos:r,cwd:o}={}){await U.mkdir(Eu,{recursive:!0});let s=H.join(Eu,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return _I(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!H.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await ao(n)??await ji(n));let i=await py([n,o,Y_]);if(i){let c=`track/${ju(e)}-${t}`;try{let l=await Na(i);await Or(i,s,c,l),await _u(s);let u=await tt(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await U.mkdir(s,{recursive:!0}),await ew(s),await _u(s),{dir:s,base:null,branch:null,kind:"repo"}}var ss="# bostrat workspace",yy=`${ss}
171
+ `),e.draft={text:u,updatedAt:Date.now()},await J()),await V({flow:"interrupt",phase:"sent",trackId:t,sessionName:e.leadBox,agent:n?.agent,dropped:r.length,escSends:s.sends,stopped:s.stopped,unsent:!!a,lostRestored:!!i,queuedRestored:l.length,shipDismissed:!!c}).catch(()=>{}),{ok:!0,interrupted:!!o,stopped:s.stopped,dropped:r.length,restored:u||null}}var OI=[300,350,400],CI=3;async function MI(t,e){let n=0;for(let r=0;r<CI;r++){await bt(t,["Escape"],{kind:"interrupt"}),n++;for(let o of OI){await Gr(o);let s=await ht(t,{lines:Tt}).catch(()=>"");if(!Ze(e,s))return{stopped:!0,sends:n}}}return{stopped:!1,sends:n}}async function NI(t){let e=gu(await D(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 PI=[250,400,500,600];async function BI(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 PI){await Gr(r);let o=await ht(t.leadBox,{lines:Tt}).catch(()=>""),s=Wo(e.agent,o);if(s&&Ml(n.text,s))return await xy(t.leadBox,e.agent,n.text.length+8),t.draft={text:n.text,updatedAt:Date.now()},it(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),await J(),n.text}return null}var DI=3e4,jI=[350,650,900];async function LI(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>DI||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!qt(r.text))return null;for(let s of jI){await Gr(s);let a;try{a=await Al(e,{})}catch{return null}if(nw(a,r))return null;let i=await ht(t.leadBox,{lines:Tt}).catch(()=>"");if(Wo(e.agent,i))return null}return t.draft={text:r.text,updatedAt:Date.now()},it(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:r.at,text:r.text,reason:"lost"}),await J(),r.text}var FI=[250,450,700],_u=2;async function UI(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=_u;r++){let o=!1;for(let s of FI){await Gr(s);let a=await ht(t.leadBox,{lines:Tt}).catch(()=>""),i=rw(e.agent,n,a,{busy:Ze(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<_u&&(await bt(t.leadBox,["Enter"],{kind:"submit-retry"}),V({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return V({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:_u,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function xy(t,e,n){for(let r=0;r<3;r++){await Us(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Gr(200);let o=await ht(t,{lines:Tt}).catch(()=>""),s=Wo(e,o);if(!s)return!0;n=s.length+8}return!1}async function Sy(t,e){if(typeof e!="string")throw new Error("text (string) required");let n=et(await W(),t);if(!n)throw new Error(`track "${t}" not found`);let r=$r(e);return r?n.draft={text:r,updatedAt:Date.now()}:delete n.draft,await J(),{ok:!0,draft:n.draft||null}}async function vy(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=et(await W(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await Z(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=xw(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await yo(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await Qe({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:is(o.agent)});let u=await Sw(a,r.leadBox,i);return it(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await J(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function Ty(t){for(let e of t){if(!e||!await Oa(e))continue;let n=await lw(e).catch(()=>null);if(!n)continue;if((await L.stat(F.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Dt(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:F.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function Vo(t){let e=[];for(let n of Dt(t?.workspace)){if(!n?.dir)continue;let r=await Fl(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Ul(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function Ay(t){let e=t;typeof e=="string"&&!F.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await oo(e)??await Li(e));let n=await Ty([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 HI(t,e,n,r){let o=`track/${Hu(e)}-${t}`,s=[];try{await L.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Ay(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await tt(c).catch(()=>null),u=F.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${F.basename(c)}-${p}`;a.add(u);let f=F.join(n,u),d=await Ca(c);await Or(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 Xt(i.base,i.dir).catch(()=>{});throw await L.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var Ey=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 _y(t,e){let n=await W(),r=et(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 Ay(typeof e=="string"?e.trim():e);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await tt(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=F.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${F.basename(s)}-${d}`;let l=F.join(o.dir,c),u=await Ca(s);await uw(s,o.branch)?await dw(s,l,o.branch):await Or(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),it(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Ey(o)}),await J(),f}async function Iy(t,e){let n=await W(),r=et(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 re.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 Fl(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Ul(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 Xt(s.base,s.dir).catch(()=>{}),await L.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),it(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Ey(o)}),await J(),{removed:s.name}}async function WI(t,e,{repo:n,repos:r,cwd:o}={}){await L.mkdir($u,{recursive:!0});let s=F.join($u,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return HI(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!F.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await oo(n)??await Li(n));let i=await Ty([n,o,fI]);if(i){let c=`track/${Hu(e)}-${t}`;try{let l=await Ca(i);await Or(i,s,c,l),await Ou(s);let u=await tt(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await L.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await L.mkdir(s,{recursive:!0}),await fw(s),await Ou(s),{dir:s,base:null,branch:null,kind:"repo"}}var rs="# bostrat workspace",Ry=`${rs}
172
172
 
173
173
  TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
174
174
  before substantive work \u2014 it is the shared cross-provider memory for this repo
@@ -335,9 +335,9 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
335
335
  statement, or omitted entirely renders no controls \u2014 so close EVERY completed
336
336
  implementation turn this way, every time. When the operator has already asked you
337
337
  to commit / push / open the PR, just do it; do not re-offer.
338
- `,RI=yy,ns=yy+Xh;async function _u(t){try{await U.writeFile(H.join(t,"AGENTS.md"),ns,{flag:"wx"})}catch{return}await MI(t,["/AGENTS.md"])}var Tu=t=>typeof t=="string"&&(t===ss||t.startsWith(`${ss}
339
- `)),$I="# bostrat multi-repo workspace";function OI(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
340
- `);return`${$I}
338
+ `,qI=Ry,ts=Ry+iw;async function Ou(t){try{await L.writeFile(F.join(t,"AGENTS.md"),ts,{flag:"wx"})}catch{return}await VI(t,["/AGENTS.md"])}var Iu=t=>typeof t=="string"&&(t===rs||t.startsWith(`${rs}
339
+ `)),KI="# bostrat multi-repo workspace";function GI(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
340
+ `);return`${KI}
341
341
 
342
342
  This directory is NOT a git repo. It is a bostrat Track workspace holding one
343
343
  git worktree per tagged repo, ALL on the same branch \`${e}\`:
@@ -349,25 +349,25 @@ push / open PRs) in each repo separately, always on that shared branch. Work
349
349
  only needs to land in the repos it actually touches \u2014 an untouched repo's
350
350
  branch simply stays at its fork point and never gets pushed. When asked to run
351
351
  or preview an app, run it from that app's own repo directory.
352
- `}async function ai(t,e,n){let r=OI(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await U.writeFile(H.join(t,o),r).catch(()=>{})}var Au=ns.replace(`${ss}
353
- `,`${ss}
352
+ `}async function oi(t,e,n){let r=GI(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await L.writeFile(F.join(t,o),r).catch(()=>{})}var Ru=ts.replace(`${rs}
353
+ `,`${rs}
354
354
 
355
355
  (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
356
356
  SCOPE: these instructions apply ONLY when your working directory is under
357
357
  ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
358
358
  When working anywhere else, ignore everything below.
359
- `);async function by(t){let{stdout:e}=await J_("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return H.resolve(t,e.trim())}async function MI(t,e){try{let n=await by(t);await U.mkdir(H.dirname(n),{recursive:!0});let r=await U.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
360
- `).includes(s));o.length&&await U.appendFile(n,(r.endsWith(`
359
+ `);async function $y(t){let{stdout:e}=await uI("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return F.resolve(t,e.trim())}async function VI(t,e){try{let n=await $y(t);await L.mkdir(F.dirname(n),{recursive:!0});let r=await L.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
360
+ `).includes(s));o.length&&await L.appendFile(n,(r.endsWith(`
361
361
  `)||!r?"":`
362
362
  `)+o.join(`
363
363
  `)+`
364
- `)}catch{}}async function CI(t,e){try{let n=await by(t),r=await U.readFile(n,"utf8"),o=r.split(`
364
+ `)}catch{}}async function zI(t,e){try{let n=await $y(t),r=await L.readFile(n,"utf8"),o=r.split(`
365
365
  `).filter(s=>s!==e);o.length!==r.split(`
366
- `).length&&await U.writeFile(n,o.join(`
367
- `))}catch{}}async function ky(){let t={refreshed:0,removed:0,codexGlobal:null};try{await U.mkdir(We,{recursive:!0}),await U.writeFile(H.join(We,"CLAUDE.md"),RI).catch(()=>{}),await U.writeFile(H.join(We,"AGENTS.md"),ns).catch(()=>{});let e=process.env.CODEX_HOME||H.join(process.env.HOME||"/tmp",".codex");if((await U.stat(e).catch(()=>null))?.isDirectory()){let r=H.join(e,"AGENTS.md"),o=await U.readFile(r,"utf8").catch(()=>null);o===null?(await U.writeFile(r,Au,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Tu(o)&&o!==Au&&(await U.writeFile(r,Au).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Eu,Hr]){let o=await U.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=H.join(r,s.name),i=H.join(a,"AGENTS.md"),c=await U.readFile(i,"utf8").catch(()=>null);c!==null&&Tu(c)&&c!==ns&&(await U.writeFile(i,ns).catch(()=>{}),t.refreshed++);let l=H.join(a,"CLAUDE.md"),u=await U.readFile(l,"utf8").catch(()=>null);u!==null&&Tu(u)&&(await U.rm(l,{force:!0}).catch(()=>{}),await CI(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function NI(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of Bt(e))n.base&&n.dir&&await Vt(n.base,n.dir).catch(()=>{});await U.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await Vt(e.base,e.dir):await U.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Cu({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 ri(o,s),i=await q(),c=cs().slice(0,8),l=await II(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(h=>(h.boxes||[]).map(w=>w.name))),f=`${t.trim()} lead`;u.has(Zi(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await _n(f,l.dir,{agentCmd:a,agent:o});zs({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a});let m={id:c,name:t.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[$y(d,"lead","live")],events:[],createdAt:Date.now()};return ct(m,"opened",{agent:o,box:d.name,skipPermissions:s,name:m.name,cwd:m.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:h,base:w,baseRef:g,repo:b})=>({name:h,base:w,baseRef:g,repo:b}))}:{}}}),i.tracks.push(m),await V(),gn(m)}var PI=new Set(["claude","codex","cursor"]);async function xy({provider:t="claude",fresh:e=!1}={}){if(!PI.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await q()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await gn(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await qr(r.id).catch(()=>{})}let o=(await ri(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=cs().slice(0,8),a=H.join(We,"setup",s);await U.mkdir(a,{recursive:!0});let i=await _n(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&zs({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:[$y(i,"lead","live")],events:[],createdAt:Date.now()};return ct(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 q()).tracks.push(c),await V(),gn(c)}async function Sy(t,e,n){await Zp(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 Kr(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Fu(a).catch(()=>{}),re.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 q(),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 ce(a.name).catch(()=>{})}async function vy(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await q(),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 Cp(s),await Sy(t,s,"track deleted"),n?await um(t).catch(()=>{}):await lm(t).catch(()=>{}),await pw(),await NI(s),e&&await Np(t).catch(()=>{}),e?await Dg(t).catch(()=>{}):await Bg(s).catch(()=>{}),ct(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}}async function qr(t){let e=await q(),n=nt(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(),ct(n,"archived",{boxes:n.boxes.length}),await V(),n.kind==="setup"?await Ty(t):DI(t).catch(()=>{}),gn(n)}var BI=9e4;async function DI(t){try{await jI(t)}catch{}await Ty(t).catch(()=>{})}async function Ty(t){let e=await q(),n=nt(e,t);if(!(!n||!n.archivedAt)){await Sy(t,n,"track archived");for(let r of n.boxes)Jn.has(r.state)||(r.state="stopped");await V()}}async function jI(t){let e=await q(),n=nt(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 Y(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Xf(n.leadBox,i.cwd,{timeoutMs:BI})),s="lead-cli"}catch{}if(r||(r=el(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 $(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 Ay(){let t=await q(),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=el(o,await L(o).catch(()=>[]));if(!s){r++;continue}o.beat={text:s,source:"derived",at:Date.now()},await $(o,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",payload:{text:s,source:"derived",backfill:!0}}).catch(()=>{}),e++}return await V(),{recorded:e,skipped:n,empty:r}}async function pn(t,e){let n=await Rt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Ey(n);if(r.length){for(let o of r)await Sr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await qr(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Ey(t){return(await be().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 mn(t,e){return Nu(t,e,"merged")}async function Nu(t,e,n="merged"){let r=await Rt(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 Ey(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Jm(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await oe(r.trackId,LI(a),{kind:"merge-notify"}),!0}catch{return!1}}var Jg={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function LI(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 ${Jg[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 ${Jg[i.outcome]??i.outcome}`).join(`
366
+ `).length&&await L.writeFile(n,o.join(`
367
+ `))}catch{}}async function Oy(){let t={refreshed:0,removed:0,codexGlobal:null};try{await L.mkdir(He,{recursive:!0}),await L.writeFile(F.join(He,"CLAUDE.md"),qI).catch(()=>{}),await L.writeFile(F.join(He,"AGENTS.md"),ts).catch(()=>{});let e=process.env.CODEX_HOME||F.join(process.env.HOME||"/tmp",".codex");if((await L.stat(e).catch(()=>null))?.isDirectory()){let r=F.join(e,"AGENTS.md"),o=await L.readFile(r,"utf8").catch(()=>null);o===null?(await L.writeFile(r,Ru,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Iu(o)&&o!==Ru&&(await L.writeFile(r,Ru).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[$u,Hr]){let o=await L.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=F.join(r,s.name),i=F.join(a,"AGENTS.md"),c=await L.readFile(i,"utf8").catch(()=>null);c!==null&&Iu(c)&&c!==ts&&(await L.writeFile(i,ts).catch(()=>{}),t.refreshed++);let l=F.join(a,"CLAUDE.md"),u=await L.readFile(l,"utf8").catch(()=>null);u!==null&&Iu(u)&&(await L.rm(l,{force:!0}).catch(()=>{}),await zI(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function JI(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of Dt(e))n.base&&n.dir&&await Xt(n.base,n.dir).catch(()=>{});await L.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await Xt(e.base,e.dir):await L.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Du({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 W(),c=as().slice(0,8),l=await WI(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(g=>g.name))),f=`${t.trim()} lead`;u.has(rc(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await In(f,l.dir,{agentCmd:a,agent:o});zs({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:[Uy(d,"lead","live")],events:[],createdAt:Date.now()};return it(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:g,baseRef:y,repo:b})=>({name:m,base:g,baseRef:y,repo:b}))}:{}}}),i.tracks.push(p),await J(),yn(p)}var XI=new Set(["claude","codex","cursor"]);async function Cy({provider:t="claude",fresh:e=!1}={}){if(!XI.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await W()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await yn(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await qr(r.id).catch(()=>{})}let o=(await ei(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=as().slice(0,8),a=F.join(He,"setup",s);await L.mkdir(a,{recursive:!0});let i=await In(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&zs({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:[Uy(i,"lead","live")],events:[],createdAt:Date.now()};return it(c,"opened",{agent:t,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await W()).tracks.push(c),await J(),yn(c)}async function My(t,e,n){await im(t).catch(()=>{});let r=os.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),os.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of Kr(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await qu(a).catch(()=>{}),re.delete(a.id);for(let a of Object.values(e.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await W(),s=new Set((o.tracks||[]).filter(a=>a.id!==t&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of e.boxes)s.has(a.name)||await ce(a.name).catch(()=>{})}async function Ny(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await W(),o=r.tracks.findIndex(a=>a.id===t);if(o===-1)throw new Error(`track "${t}" not found`);let s=r.tracks[o];return e||await Hp(s),await My(t,s,"track deleted"),n?await bm(t).catch(()=>{}):await ym(t).catch(()=>{}),await Tw(),await JI(s),e&&await Wp(t).catch(()=>{}),e?await zg(t).catch(()=>{}):await Vg(s).catch(()=>{}),it(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await J(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}async function qr(t){let e=await W(),n=et(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(),it(n,"archived",{boxes:n.boxes.length}),await J(),n.kind==="setup"?await Py(t):ZI(t).catch(()=>{}),yn(n)}var YI=9e4;async function ZI(t){try{await QI(t)}catch{}await Py(t).catch(()=>{})}async function Py(t){let e=await W(),n=et(e,t);if(!(!n||!n.archivedAt)){await My(t,n,"track archived");for(let r of n.boxes)Xn.has(r.state)||(r.state="stopped");await J()}}async function QI(t){let e=await W(),n=et(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 Z(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await mp(n.leadBox,i.cwd,{timeoutMs:YI})),s="lead-cli"}catch{}if(r||(r=sl(n,await D(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 J()}async function By(){let t=await W(),e=0,n=0,r=0;for(let o of t.tracks){if(!o.archivedAt||o.kind==="setup"||o.beat?.text){n++;continue}let s=sl(o,await D(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 J(),{recorded:e,skipped:n,empty:r}}async function mn(t,e){let n=await $t(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Dy(n);if(r.length){for(let o of r)await Sr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await qr(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Dy(t){return(await ye().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 hn(t,e){return ju(t,e,"merged")}async function ju(t,e,n="merged"){let r=await $t(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 Dy(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await sh(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await oe(r.trackId,tR(a),{kind:"merge-notify"}),!0}catch{return!1}}var ay={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function tR(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 ${ay[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 ${ay[i.outcome]??i.outcome}`).join(`
368
368
  `),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}
369
369
  ${s}
370
- ${a}`}async function Pu(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||Jn.has(e.state)||await Y(e.name))return!1;let n=Zg(e.agent,e.sessionId);if(!await js(n))return ct(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" not available on this box`}),!1;try{let r=await _n(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=oo(r.hookToken),zs({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",ct(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(r){return ct(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(r?.message||r)}),!1}}async function _y(t){let e=await q(),n=nt(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,ct(n,"unarchived",{}),await Pu(n),await V(),gn(n)}async function Iy(){let t=await q(),e=new Set((await En()).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 ce(a.name).catch(()=>{}),ct(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Jn.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await V(),r}async function Ry(t){let e=await q(),n=nt(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(Jn.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await Y(n.leadBox)){let a=await gt(n.leadBox,{lines:At}).catch(()=>"");if(Ks(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await ce(n.leadBox),r.state="stopped"}let s=await Pu(n);if(await V(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return gn(n)}var $y=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:oo(t.hookToken),createdAt:Date.now()});function Oy(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,us=t=>as.get(t)||(Se?nt(Se,t)?.handoff:null)||null;async function ti(t,e){let n=await q(),r=nt(n,t);r&&(r.handoff={...r.handoff||{},...e},await V())}function FI(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 My(){let t=await q(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=FI(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await Y(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=Oy(l),d=f[f.length-1],m=d.agent,h=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},ct(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:m,toAgent:r.toAgent,fromSessionId:h,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 ce(i).catch(()=>{}),a&&await ce(a).catch(()=>{})}return e&&await V(),e}async function Bu(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=nt(await q(),t);if(!s)throw new Error(`track "${t}" not found`);let a=await ri(e,n),i=await Y(s.leadBox);if(!i)throw new Error(`lead box "${s.leadBox}" is not running`);let c={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:Qg(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return as.set(t,c),await ti(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),UI(c,s,i,a,e).catch(async l=>{c.error=l?.message||String(l),c.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${c.step}": ${c.error}`),await ti(t,{step:c.step,error:c.error,finishedAt:c.finishedAt}).catch(()=>{}),G({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),G({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function UI(t,e,n,r,o){let s=async(A,C={})=>{t.step=A,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${A}`),await ti(t.trackId,{step:A,toBox:t.toBox}),await G({flow:"lead-handoff",phase:A,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:C.sessionName,...C})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await q(),i=nt(a,t.trackId)?.boxes.find(A=>A.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 rp(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await rl(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await Qe({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:ls(o)});let d=await gt(t.fromBox,{lines:At}).catch(()=>"");if(!Gs(o,d)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let A=Zg(c,l,{skipPermissions:t.skipPermissions});await rl(t.fromBox,{agentCmd:A,agent:c,resumeSessionId:l,pinned:u});let C=await q(),z=nt(C,t.trackId);if(z){let O=z.boxes.find(F=>F.name===t.fromBox);O&&(O.state="live"),ct(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 m=await q(),h=nt(m,t.trackId);if(!h)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let w=h.boxes.find(A=>A.name===t.fromBox);if(!w)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let g=Date.now(),b=Oy(w),S=b[b.length-1];S.endedAt=g,t.reason&&(S.endReason=t.reason),b.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:g,endedAt:null}),w.agent=o,w.sessionId=f.sessionId,w.pinned=f.pinned,w.state="live",w.role="lead",h.pendingHandoffHydrate={artifact:oi,at:g},ct(h,"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 V(),await s("done",{sessionName:t.fromBox}),await ti(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var HI=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,WI=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Hr=H.join(We,"worktrees"),Fr=[rs,Qa],re=new Map;var Kr=t=>{let e=new Map([...re.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=Se?nt(Se,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)},Du=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 qI(t,e,n){let r=await q(),o=nt(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await V())}var Wr=t=>qI(t.trackId,t.id,Du(t));async function Ur(t,e){t.step=e,await Wr(t)}function KI(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 GI(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function zI(t,e,n){if(!GI(n)||re.has(e)||!await Y(n.box).catch(()=>null))return!1;try{await U.access(n.dir)}catch{return!1}let o=await Y(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return re.set(e,s),Dy(s,o).catch(a=>Py(s,a)),!0}async function Cy(){let t=await q(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(re.has(r))continue;if(await zI(n,r,o)){e=!0,ct(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=KI(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Jn.has(i.state)&&(i.state="canceled"),ct(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 Xg(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Jn.has(r.state)||re.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?H.join(Hr,`${n.id}-${r.subtaskId}`):null;await G({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await Xg(n,r.name,o)}}return e=await VI(t)||e,e&&await V(),e}async function Xg(t,e,n){if(e&&await ce(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of Bt(t.workspace)){let s=H.join(n,o.name);await Vt(o.base,s).then(()=>!0,()=>!1)||await Vt(o.dir,s).catch(()=>{})}await U.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await Y(t.leadBox).catch(()=>null);r?.cwd&&await Vt(r.cwd,n).catch(()=>{})}async function VI(t){let e;try{e=await U.readdir(Hr)}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 re.values())o.dir&&n.add(H.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=H.join(Hr,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Bt(a.workspace))await Vt(i.base,H.join(s,i.name)).catch(()=>{});await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await Y(a.leadBox).catch(()=>null):null;i?.cwd?await Vt(i.cwd,s).catch(()=>{}):await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await G({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var ju=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",is=class extends Error{};async function Ny(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!$r(n))throw new Error("task (string) required");let s=await ri(e,r),a=nt(await q(),t);if(!a)throw new Error(`track "${t}" not found`);let i=await Y(a.leadBox);if(!i)throw new Error(`lead box "${a.leadBox}" is not running`);if(!i.cwd)throw new Error("can't resolve the lead's working directory");let c=a.workspace?.kind==="multi",l=null;if(c){let d=Bt(a.workspace);if(!d.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of d)if(!m?.dir||!await Ca(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);l=d.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await Ca(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let u=cs().slice(0,8),f={id:u,trackId:t,trackName:a.name,agent:e,task:$r(n),skipPermissions:r,reason:Qg(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${ju(a.name)}-${u}`,base:null,repos:l,dir:H.join(Hr,`${t}-${u}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return re.set(u,f),await Wr(f),JI(f,i,s).catch(d=>Py(f,d)),f}async function Py(t,e){t.failedStep=t.step,t.error=e instanceof is?"canceled":e?.message||String(e),t.step=e instanceof is?"canceled":"error",t.finishedAt=Date.now(),await Fu(t).catch(()=>{}),await Wr(t).catch(()=>{}),await QI(t).catch(()=>{})}function Lu(t){let e=re.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 By(t){let e=re.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 JI(t,e,n){if(await Ur(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await zo(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await Wr(t)}else t.base=await zo(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await Ur(t,"spawn"),await U.mkdir(Hr,{recursive:!0}),t.repos){await U.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Or(a.leadDir,H.join(t.dir,a.name),t.branch,a.base);let s=Bt((nt(await q(),t.trackId)||{}).workspace);await ai(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 Or(e.cwd,t.dir,t.branch,t.base),await _u(t.dir);await XI(t,e);let r=t.trackName.slice(0,38).trim(),o=await _n(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await YI(t,o),await ty(t.trackId,o.name,eI(t.task,!!t.repos),{kind:"subtask"}),await Dy(t,e)}async function Dy(t,e){await Ur(t,"working");let n=H.join(t.dir,Qa);if(await eR(n,t),t.canceled)throw new is;if(await Ur(t,"merge"),t.repos){let o={files:0,insertions:0,deletions:0},s=[],a=[],i=!0;for(let c of t.repos){let l=H.join(t.dir,c.name);await Pl(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await Ll(c.leadDir,c.base,t.branch,Fr),c.files=await Fl(c.leadDir,c.base,t.branch,Fr);let u=await Ul(c.leadDir,c.base,t.branch,Fr);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=>H.join(c.name,f))),a.push(...c.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${c.name}: ${f}`:H.join(c.name,f))),u.applied||(i=!1)}t.stat=o,t.files=s,t.conflicts=a,t.applied=i}else{await Pl(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Ll(e.cwd,t.base,t.branch,Fr),t.files=await Fl(e.cwd,t.base,t.branch,Fr);let o=await Ul(e.cwd,t.base,t.branch,Fr);t.applied=o.applied,t.conflicts=o.conflicts}await Wr(t);let r=await tR(t,e,n);await Ur(t,"resume"),await ty(t.trackId,t.leadBox,nI(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await Ur(t,"reap"),await Fu(t),t.step="done",t.finishedAt=Date.now(),await ZI(t),await Wr(t)}async function XI(t,e){let n=H.join(t.dir,H.dirname(rs));await U.mkdir(n,{recursive:!0});let r="";try{r=await ff(e.cwd,e.sessionId)}catch{}if(!r)try{r=await U.readFile(H.join(e.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${t.id}
370
+ ${a}`}async function Lu(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||Xn.has(e.state)||await Z(e.name))return!1;let n=ly(e.agent,e.sessionId);if(!await Ls(n))return it(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" not available on this box`}),!1;try{let r=await In(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=no(r.hookToken),zs({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",it(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(r){return it(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(r?.message||r)}),!1}}async function jy(t){let e=await W(),n=et(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,it(n,"unarchived",{}),await Lu(n),await J(),yn(n)}async function Ly(){let t=await W(),e=new Set((await _n()).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 ce(a.name).catch(()=>{}),it(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Xn.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await J(),r}async function Fy(t){let e=await W(),n=et(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(Xn.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await Z(n.leadBox)){let a=await ht(n.leadBox,{lines:Tt}).catch(()=>"");if(Gs(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await ce(n.leadBox),r.state="stopped"}let s=await Lu(n);if(await J(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return yn(n)}var Uy=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:no(t.hookToken),createdAt:Date.now()});function Hy(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 os=new Map,cs=t=>os.get(t)||(xe?et(xe,t)?.handoff:null)||null;async function Za(t,e){let n=await W(),r=et(n,t);r&&(r.handoff={...r.handoff||{},...e},await J())}function eR(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 Wy(){let t=await W(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=eR(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await Z(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=Hy(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let g=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:g,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=g,l.state="live",n.pendingHandoffHydrate={artifact:ni,at:u},it(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 V({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 V({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await ce(i).catch(()=>{}),a&&await ce(a).catch(()=>{})}return e&&await J(),e}async function Fu(t,e,n=!0,r=null){let o=os.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=et(await W(),t);if(!s)throw new Error(`track "${t}" not found`);let a=await ei(e,n),i=await Z(s.leadBox);if(!i)throw new Error(`lead box "${s.leadBox}" is not running`);let c={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:uy(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return os.set(t,c),await Za(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),nR(c,s,i,a,e).catch(async l=>{c.error=l?.message||String(l),c.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${c.step}": ${c.error}`),await Za(t,{step:c.step,error:c.error,finishedAt:c.finishedAt}).catch(()=>{}),V({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),V({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function nR(t,e,n,r,o){let s=async(E,M={})=>{t.step=E,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${E}`),await Za(t.trackId,{step:E,toBox:t.toBox}),await V({flow:"lead-handoff",phase:E,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:M.sessionName,...M})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await W(),i=et(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 xp(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await il(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await Qe({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:is(o)});let d=await ht(t.fromBox,{lines:Tt}).catch(()=>"");if(!Vs(o,d)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let E=ly(c,l,{skipPermissions:t.skipPermissions});await il(t.fromBox,{agentCmd:E,agent:c,resumeSessionId:l,pinned:u});let M=await W(),z=et(M,t.trackId);if(z){let $=z.boxes.find(j=>j.name===t.fromBox);$&&($.state="live"),it(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 J()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let p=await W(),m=et(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 g=m.boxes.find(E=>E.name===t.fromBox);if(!g)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let y=Date.now(),b=Hy(g),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}),g.agent=o,g.sessionId=f.sessionId,g.pinned=f.pinned,g.state="live",g.role="lead",m.pendingHandoffHydrate={artifact:ni,at:y},it(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 J(),await s("done",{sessionName:t.fromBox}),await Za(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var rR=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,oR=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Hr=F.join(He,"worktrees"),Fr=[es,Ya],re=new Map;var Kr=t=>{let e=new Map([...re.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=xe?et(xe,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)},Uu=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 sR(t,e,n){let r=await W(),o=et(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await J())}var Wr=t=>sR(t.trackId,t.id,Uu(t));async function Ur(t,e){t.step=e,await Wr(t)}function aR(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 iR(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function cR(t,e,n){if(!iR(n)||re.has(e)||!await Z(n.box).catch(()=>null))return!1;try{await L.access(n.dir)}catch{return!1}let o=await Z(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return re.set(e,s),zy(s,o).catch(a=>Gy(s,a)),!0}async function qy(){let t=await W(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(re.has(r))continue;if(await cR(n,r,o)){e=!0,it(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await V({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=aR(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Xn.has(i.state)&&(i.state="canceled"),it(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await V({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await iy(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Xn.has(r.state)||re.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?F.join(Hr,`${n.id}-${r.subtaskId}`):null;await V({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await iy(n,r.name,o)}}return e=await lR(t)||e,e&&await J(),e}async function iy(t,e,n){if(e&&await ce(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of Dt(t.workspace)){let s=F.join(n,o.name);await Xt(o.base,s).then(()=>!0,()=>!1)||await Xt(o.dir,s).catch(()=>{})}await L.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await Z(t.leadBox).catch(()=>null);r?.cwd&&await Xt(r.cwd,n).catch(()=>{})}async function lR(t){let e;try{e=await L.readdir(Hr)}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 re.values())o.dir&&n.add(F.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=F.join(Hr,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Dt(a.workspace))await Xt(i.base,F.join(s,i.name)).catch(()=>{});await L.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await Z(a.leadBox).catch(()=>null):null;i?.cwd?await Xt(i.cwd,s).catch(()=>{}):await L.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await V({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Hu=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",ss=class extends Error{};async function Ky(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!$r(n))throw new Error("task (string) required");let s=await ei(e,r),a=et(await W(),t);if(!a)throw new Error(`track "${t}" not found`);let i=await Z(a.leadBox);if(!i)throw new Error(`lead box "${a.leadBox}" is not running`);if(!i.cwd)throw new Error("can't resolve the lead's working directory");let c=a.workspace?.kind==="multi",l=null;if(c){let d=Dt(a.workspace);if(!d.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let p of d)if(!p?.dir||!await Oa(p.dir))throw new Error(`tagged checkout "${p?.name??"?"}" is missing from the lead workspace (${p?.dir??"no dir"})`);l=d.map(p=>({name:p.name,leadDir:p.dir,base:null}))}else if(!await Oa(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let u=as().slice(0,8),f={id:u,trackId:t,trackName:a.name,agent:e,task:$r(n),skipPermissions:r,reason:uy(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${Hu(a.name)}-${u}`,base:null,repos:l,dir:F.join(Hr,`${t}-${u}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return re.set(u,f),await Wr(f),uR(f,i,s).catch(d=>Gy(f,d)),f}async function Gy(t,e){t.failedStep=t.step,t.error=e instanceof ss?"canceled":e?.message||String(e),t.step=e instanceof ss?"canceled":"error",t.finishedAt=Date.now(),await qu(t).catch(()=>{}),await Wr(t).catch(()=>{}),await mR(t).catch(()=>{})}function Wu(t){let e=re.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 Vy(t){let e=re.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 uR(t,e,n){if(await Ur(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await Ko(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await Wr(t)}else t.base=await Ko(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await Ur(t,"spawn"),await L.mkdir(Hr,{recursive:!0}),t.repos){await L.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Or(a.leadDir,F.join(t.dir,a.name),t.branch,a.base);let s=Dt((et(await W(),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 Or(e.cwd,t.dir,t.branch,t.base),await Ou(t.dir);await dR(t,e);let r=t.trackName.slice(0,38).trim(),o=await In(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await fR(t,o),await dy(t.trackId,o.name,wI(t.task,!!t.repos),{kind:"subtask"}),await zy(t,e)}async function zy(t,e){await Ur(t,"working");let n=F.join(t.dir,Ya);if(await wR(n,t),t.canceled)throw new ss;if(await Ur(t,"merge"),t.repos){let o={files:0,insertions:0,deletions:0},s=[],a=[],i=!0;for(let c of t.repos){let l=F.join(t.dir,c.name);await Ll(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await Wl(c.leadDir,c.base,t.branch,Fr),c.files=await ql(c.leadDir,c.base,t.branch,Fr);let u=await Kl(c.leadDir,c.base,t.branch,Fr);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=>F.join(c.name,f))),a.push(...c.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${c.name}: ${f}`:F.join(c.name,f))),u.applied||(i=!1)}t.stat=o,t.files=s,t.conflicts=a,t.applied=i}else{await Ll(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Wl(e.cwd,t.base,t.branch,Fr),t.files=await ql(e.cwd,t.base,t.branch,Fr);let o=await Kl(e.cwd,t.base,t.branch,Fr);t.applied=o.applied,t.conflicts=o.conflicts}await Wr(t);let r=await hR(t,e,n);await Ur(t,"resume"),await dy(t.trackId,t.leadBox,gI(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await Ur(t,"reap"),await qu(t),t.step="done",t.finishedAt=Date.now(),await pR(t),await Wr(t)}async function dR(t,e){let n=F.join(t.dir,F.dirname(es));await L.mkdir(n,{recursive:!0});let r="";try{r=await $f(e.cwd,e.sessionId)}catch{}if(!r)try{r=await L.readFile(F.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}
371
371
 
372
372
  **Delegated task:** ${t.task}
373
373
 
@@ -378,49 +378,50 @@ ${a}`}async function Pu(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name
378
378
  ## Conversation so far (lead \u2192 you)
379
379
 
380
380
  ${r}
381
- `;await U.writeFile(H.join(t.dir,rs),o)}async function YI(t,e){let n=await q(),r=nt(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:oo(e.hookToken),createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),ct(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]||{},...Du(t)},await V())}async function ZI(t){let e=await q(),n=nt(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),ct(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 QI(t){let e=await q(),n=nt(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"),ct(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]||{},...Du(t)},await V()}async function tR(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=H.join(e.cwd,r);await U.mkdir(H.dirname(o),{recursive:!0}),await U.copyFile(n,o)}catch{}return r}async function Fu(t){if(t.box&&await ce(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await Vt(e.leadDir,H.join(t.dir,e.name)).catch(()=>{});await U.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await Y(t.leadBox).catch(()=>null))?.cwd;n&&await Vt(n,t.dir).catch(()=>{})}}async function eR(t,e){let n=(e.startedAt||Date.now())+HI;for(;;){if(e.canceled||e.forceMerge)return;try{await U.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Gr(WI)}}function Gr(t){return new Promise(e=>setTimeout(e,t))}import ds from"node:fs/promises";import li from"node:path";import aR from"node:os";import nR from"ws";var jy=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function ii(t,e,n={}){let r=await fetch(`${jy()}/${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 Uu=(t,e)=>ii("chat.postMessage",t,e),zr=(t,e)=>ii("chat.update",t,e);async function Dt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function rR(t,e,n={}){let r=await fetch(`${jy()}/${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 Ly(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await rR("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 ii("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var oR=2200;function Fy({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 ii("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new nR(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{}},m=!1,h=setTimeout(()=>{m=!0,d()},oR);Promise.resolve().then(()=>e(f)).then(w=>{clearTimeout(h),!m&&(m=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(h),m||(m=!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 Wy=()=>process.env.BOSTRAT_STATE_DIR||li.join(process.env.HOME||aR.tmpdir(),".bostrat"),Gu=()=>li.join(Wy(),"slack.json"),ci=null,Hu=null,Wu=Promise.resolve();async function Vu(){return ci||(Hu??=ds.readFile(Gu(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>ci=t).finally(()=>Hu=null),Hu)}async function Uy(){let t=JSON.stringify({threads:ci},null,2),e=`${Gu()}.${process.pid}.tmp`;await ds.mkdir(Wy(),{recursive:!0}),await ds.writeFile(e,t),await ds.rename(e,Gu())}async function qe(){Wu=Wu.then(Uy,Uy),await Wu.catch(()=>{})}async function Ju(t){return(await Vu())[t]??null}async function iR(t,e){let n=await Vu();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var X=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");function fs(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function cR(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return fs(n.replace(/\n+/g," "),e)}function qy(t){let e=`*${X(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${X(t.repoLabel)} \xB7 \`${X(t.branch||"?")}\` \xB7 lead *${X(t.lead)}*`,r=t.kickoff?`> ${X(fs(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
382
- ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${ba(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function zu(t,e){await zr(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:qy(e)})}async function Yn(t,e,n,r=void 0){return Uu(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var Hy=new Set;function Xn(t){Hy.add(t),t.catch(()=>{}).finally(()=>Hy.delete(t))}var pt=t=>({response_type:"ephemeral",text:t}),lR="*/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._",uR=["claude","cursor","codex"];function qu(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 Ku=(t,e)=>pt(`No live Track matches \u201C${X(e)}\u201D. Live: ${t.map(n=>`\`${X(n.name)}\``).join(" \xB7 ")||"none"}`);async function Ky(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 Dt(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 Dt(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?li.basename(c.base):"fresh workspace"),lead:"claude"},u=await Uu(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:qy(l),unfurl_links:!1});l.ts=u.ts,(await Vu())[i.id]=l,await qe(),await Dt(o,{...pt(`\u2713 *${X(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Dt(o,{...pt(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function dR(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Ju(n.id);s&&(s.archivedAt=Date.now(),await qe(),await zu(e,s).catch(()=>{})),await Dt(r,{...pt(`\u2713 *${X(n.name)}* archived.`),replace_original:!0})}catch(s){await Dt(r,{...pt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function fR(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Dt(o,{...pt(`\u2713 Handing *${X(n.name)}* to ${X(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Dt(o,{...pt(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function pR(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 Dt(r,{...pt(`\u2713 Stopped *${X(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await Dt(r,{...pt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function mR(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:fs(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:t}),text:{type:"mrkdwn",text:`Repo for *${X(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function hR(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?li.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${X(s.name)}* \u2014 ${X(i)}${a.branch?` \`${X(a.branch)}\``:""} \xB7 lead ${X(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
381
+ `;await L.writeFile(F.join(t.dir,es),o)}async function fR(t,e){let n=await W(),r=et(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:no(e.hookToken),createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),it(r,"subtask-delegated",{to:e.name,toAgent:t.agent,task:t.task,subtaskId:t.id,skipPermissions:t.skipPermissions,reason:t.reason}),r.subtasks=r.subtasks||{},r.subtasks[t.id]={...r.subtasks[t.id]||{},...Uu(t)},await J())}async function pR(t){let e=await W(),n=et(e,t.trackId);if(!n)return;let r=n.boxes.find(o=>o.subtaskId===t.id);r&&(r.state=t.conflicts.length?"conflict":"merged",r.stat=t.stat),it(n,"subtask-merged",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,stat:t.stat,conflicts:t.conflicts.length,files:t.files,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),await J()}async function mR(t){let e=await W(),n=et(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),it(n,r?"subtask-canceled":"subtask-failed",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,failedStep:t.failedStep||t.step,error:t.error,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[t.id]={...n.subtasks[t.id]||{},...Uu(t)},await J()}async function hR(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=F.join(e.cwd,r);await L.mkdir(F.dirname(o),{recursive:!0}),await L.copyFile(n,o)}catch{}return r}async function qu(t){if(t.box&&await ce(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await Xt(e.leadDir,F.join(t.dir,e.name)).catch(()=>{});await L.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await Z(t.leadBox).catch(()=>null))?.cwd;n&&await Xt(n,t.dir).catch(()=>{})}}async function wR(t,e){let n=(e.startedAt||Date.now())+rR;for(;;){if(e.canceled||e.forceMerge)return;try{await L.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Gr(oR)}}function Gr(t){return new Promise(e=>setTimeout(e,t))}import ls from"node:fs/promises";import ii from"node:path";import xR from"node:os";import gR from"ws";var Jy=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function si(t,e,n={}){let r=await fetch(`${Jy()}/${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 Ku=(t,e)=>si("chat.postMessage",t,e),Vr=(t,e)=>si("chat.update",t,e);async function jt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function yR(t,e,n={}){let r=await fetch(`${Jy()}/${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 Xy(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await yR("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 si("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var bR=2200;function Yy({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 si("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new gR(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=g=>{try{o.send(JSON.stringify(g?{envelope_id:f.envelope_id,payload:g}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},bR);Promise.resolve().then(()=>e(f)).then(g=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?g:void 0))},g=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${g?.message||g}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var tb=()=>process.env.BOSTRAT_STATE_DIR||ii.join(process.env.HOME||xR.tmpdir(),".bostrat"),Xu=()=>ii.join(tb(),"slack.json"),ai=null,Gu=null,Vu=Promise.resolve();async function Zu(){return ai||(Gu??=ls.readFile(Xu(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>ai=t).finally(()=>Gu=null),Gu)}async function Zy(){let t=JSON.stringify({threads:ai},null,2),e=`${Xu()}.${process.pid}.tmp`;await ls.mkdir(tb(),{recursive:!0}),await ls.writeFile(e,t),await ls.rename(e,Xu())}async function We(){Vu=Vu.then(Zy,Zy),await Vu.catch(()=>{})}async function Qu(t){return(await Zu())[t]??null}async function SR(t,e){let n=await Zu();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 us(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function vR(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return us(n.replace(/\n+/g," "),e)}function eb(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(us(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
382
+ ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${ya(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Yu(t,e){await Vr(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:eb(e)})}async function Zn(t,e,n,r=void 0){return Ku(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var Qy=new Set;function Yn(t){Qy.add(t),t.catch(()=>{}).finally(()=>Qy.delete(t))}var ut=t=>({response_type:"ephemeral",text:t}),TR="*/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._",AR=["claude","cursor","codex"];function zu(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 Ju=(t,e)=>ut(`No live Track matches \u201C${Y(e)}\u201D. Live: ${t.map(n=>`\`${Y(n.name)}\``).join(" \xB7 ")||"none"}`);async function nb(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 jt(o,{...ut(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await jt(o,{...ut("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?ii.basename(c.base):"fresh workspace"),lead:"claude"},u=await Ku(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:eb(l),unfurl_links:!1});l.ts=u.ts,(await Zu())[i.id]=l,await We(),await jt(o,{...ut(`\u2713 *${Y(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await jt(o,{...ut(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function ER(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Qu(n.id);s&&(s.archivedAt=Date.now(),await We(),await Yu(e,s).catch(()=>{})),await jt(r,{...ut(`\u2713 *${Y(n.name)}* archived.`),replace_original:!0})}catch(s){await jt(r,{...ut(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function _R(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await jt(o,{...ut(`\u2713 Handing *${Y(n.name)}* to ${Y(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await jt(o,{...ut(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function IR(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 jt(r,{...ut(`\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 jt(r,{...ut(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function RR(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:us(`${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 $R(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?ii.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(`
383
383
  `)+(o?`
384
- _${o} archived_`:"")}async function wR(t,e){let n=await Pn();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?(Xn(Ky(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),pt(`Starting *${X(u)}* on ${X(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:mR(u,i)}}case"archive":{if(!a.length)return pt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=qu(await r.listTracks(),a.join(" "));return i?(Xn(dR(t,n,{track:i,responseUrl:e.response_url})),pt(`Archiving *${X(i.name)}*\u2026`)):Ku(c,a.join(" "))}case"handoff":{let i=r.knownAgents??uR,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}=qu(await r.listTracks(),l);return u?(Xn(fR(t,n,{track:u,agent:c,responseUrl:e.response_url})),pt(`Handing *${X(u.name)}* to ${X(c)}\u2026`)):Ku(f,l)}case"stop":{if(!a.length)return pt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=qu(await r.listTracks(),a.join(" "));return i?(Xn(pR(t,n,{track:i,responseUrl:e.response_url})),pt(`Stopping *${X(i.name)}*\u2026`)):Ku(c,a.join(" "))}case"list":return pt(await hR(r));default:return pt(lR)}}async function gR(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Ju(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 qe(),await zr(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${X(l||"question")}~
385
- \u2713 *${X(a)}*`}}]}).catch(()=>{})}}catch(c){await Dt(n.response_url,pt(`Couldn't answer: ${c?.message||c}`))}}async function yR(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,ja(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 Dt(n.response_url,pt(`Couldn't act on the offer: ${c?.message||c}`))}}async function bR(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await Pn();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 Dt(e.response_url,{...pt(`Starting *${X(s)}*\u2026`),replace_original:!0}),Xn(Ky(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"?Xn(gR(t,r,e,o)):n.action_id==="bostrat-ship"&&Xn(yR(t,r,e,o))}async function kR(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 iR(e.channel,e.thread_ts);if(!n)return;let r=await Pn();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await Yn(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function xR(t){return async e=>{if(e.type==="slash_commands")return wR(t,e.payload);if(e.type==="interactive"){await bR(t,e.payload);return}if(e.type==="events_api"){await kR(t,e.payload?.event);return}}}var SR=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 vR(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${X(e.question||"The lead needs an answer")}`}}],r=(e.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:fs(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 TR(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await zr(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${X(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Yn(t,e,r.question||"The lead needs an answer",vR(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await qe()}async function AR(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 qe(),await zr(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${X(s||"question")}~
386
- \u2713 _answered_`}}]})}async function ER(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 Yn(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${X(fs(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await qe()}async function _R(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await qe();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await zr(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function IR(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??RR)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Ly(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${X(o.name)}*${a}${o.group?` (group \`${X(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${X(a.name)}*`).join(", ");await Yn(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${X(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function RR(t,e){let n=await en(t);if(!n)return null;let r=Ie(n,e);if(!r)return null;let o=await ds.readFile(Ut(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function $R(t,e){if(!SR.has(e.type))return;let n=await Ju(e.trackId);if(!n)return;let r=await Pn();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 qe(),await zu(r,n);return;case"turn.agent":{if(!o.text)return;let s=cR(o.text);if(!s)return;await Yn(r,n,s,[{type:"section",text:{type:"mrkdwn",text:X(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${ba(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return TR(r,n,e);case"prompt.answered":return AR(r,n,e);case"pr.offered":return ER(r,n,e);case"pr.offer.answered":return _R(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 Yn(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 qe(),await zu(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Yn(r,n,`Live preview ${o.name}: ${xa(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${X(o.name)}*: ${xa(o.slug)}
387
- _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return IR(r,n,e,t.deps)}}function Gy(t){let e={deps:t};zt.on("event",s=>{$R(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Pn()||(r&&clearInterval(r),r=null,n=Fy({getToken:async()=>(await Pn())?.appToken,onEnvelope:xR(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import OR from"node:fs/promises";function MR(t){let e=new Map;for(let n of String(t||"").split(`
388
- `)){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 CR(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 NR(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Ee(e.cwd,e.sessionId):e?.cwd?nc(e.cwd):null}async function zy(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await $(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":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 PR(r,o,t,i);break;default:break}return a}async function PR(t,e,n,r){let o=[],s=NR(n,e);if(s)try{o=MR(await OR.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=CR(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",m=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let h of Object.keys(m.tokens))m.tokens[h]+=f.tokens[h];m.messageIds.push(f.messageId),u.set(d,m)}for(let[f,d]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:le(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 jR from"node:fs";import Xu from"node:fs/promises";import LR from"node:http";import Vr from"node:path";import di from"node:os";import{createRequire as FR}from"node:module";import Yu from"ws";import BR from"node:path";import DR from"node:os";var Vy=process.env.BOSTRAT_STATE_DIR||BR.join(process.env.HOME||DR.tmpdir(),".bostrat");function Jy(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 Xy(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 ui=(()=>{try{return FR(import.meta.url)("../package.json").version||null}catch{return null}})(),UR=process.env.BOSTRAT_STATE_DIR||Vr.join(process.env.HOME||di.tmpdir(),".bostrat");function HR(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 WR({home:t=process.env.HOME||di.homedir(),env:e=process.env}={}){let n=r=>{try{return jR.statSync(r),!0}catch{return!1}};return{claude:n(Vr.join(t,".claude",".credentials.json")),codex:n(Vr.join(e.CODEX_HOME||Vr.join(t,".codex"),"auth.json"))}}var It=1,Yy=1e3,qR=3e4,KR=6e4,GR=1500,zR=500,Zy=100,VR=2e3,JR=25e3;function Qy({url:t,token:e,nodeId:n=di.hostname(),name:r=di.hostname(),listTracks:o,detectAgents:s=null,selfUrl:a=`http://127.0.0.1:${process.env.PORT||3362}`,log:i=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=Xy(t);let c=a.replace(/^http/,"ws"),l=new Map,u=new Map,f=Vr.join(UR,"plane-link","cursor.json"),d={},m=new Map,h=null,w=!1,g=!1,b=Yy,S=null,A=null,C=null,z=null,O=!1;async function F(){try{d=JSON.parse(await Xu.readFile(f,"utf8"))}catch{d={}}}function T(){O=!0,!C&&(C=setTimeout(async()=>{if(C=null,!!O){O=!1;try{await Xu.mkdir(Vr.dirname(f),{recursive:!0}),await Xu.writeFile(f,JSON.stringify(d))}catch(I){i.error("[plane-link] cursor flush failed:",I?.message||I)}}},VR),C.unref?.())}function M(I){return!h||h.readyState!==Yu.OPEN?!1:(h.send(JSON.stringify(I)),!0)}function W(I){for(let{trackId:R,event:_}of I)m.set(_.id,{trackId:R,seq:_.seq});return M({v:It,ch:"events",type:"append",events:I})}async function st(){let I;try{I=await o()}catch(R){i.error("[plane-link] backfill listTracks failed:",R?.message||R);return}M({v:It,ch:"ctl",type:"tracks",ids:I.map(R=>R.id).filter(R=>typeof R=="string")});for(let R of I){if(!w)return;let _;try{_=await L(R)}catch{continue}let P=d[R.id]??0,et=_.filter(wt=>wt.seq>P&&typeof wt.id=="string");for(let wt=0;wt<et.length;wt+=Zy){let bt=et.slice(wt,wt+Zy).map($t=>({trackId:R.id,event:$t}));if(!W(bt))return}}}function y(I){!w||!I?.trackId||typeof I.id!="string"||W([{trackId:I.trackId,event:I}])}let p=new Map;async function v(I){let{reqId:R,method:_,path:P,headers:et={},body:wt}=I,bt=Object.fromEntries(Object.entries(et).filter(([,Kt])=>Kt!=null)),$t=new AbortController;p.set(R,$t);try{if(I.host){await E(I,bt,$t);return}let Kt=await fetch(a+P,{method:_,headers:bt,body:wt?Buffer.from(wt,"base64"):void 0,signal:$t.signal}),Xt=Object.fromEntries(Kt.headers);for(let k of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Xt[k];if(Xt["content-type"]||(Xt["content-type"]="application/octet-stream"),M({v:It,ch:"control",type:"http_head",reqId:R,status:Kt.status,headers:Xt}),Kt.body){for await(let k of Kt.body)if(!M({v:It,ch:"control",type:"http_data",reqId:R,data:Buffer.from(k).toString("base64")}))break}M({v:It,ch:"control",type:"http_end",reqId:R,error:null})}finally{p.delete(R)}}function E(I,R,_){let{reqId:P,method:et,path:wt,body:bt}=I,$t=new URL(a);return new Promise(Kt=>{let Xt=LR.request({host:$t.hostname,port:$t.port,method:et,path:wt,headers:{...R,host:I.host},signal:_.signal},k=>{let{connection:K,"keep-alive":Zn,"transfer-encoding":As,upgrade:Qn,...Ri}=k.headers;M({v:It,ch:"control",type:"http_head",reqId:P,status:k.statusCode||502,headers:Ri}),k.on("data",Dk=>{M({v:It,ch:"control",type:"http_data",reqId:P,data:Buffer.from(Dk).toString("base64")})||k.destroy()}),k.on("end",()=>{M({v:It,ch:"control",type:"http_end",reqId:P,error:null}),Kt()}),k.on("error",()=>{M({v:It,ch:"control",type:"http_end",reqId:P,error:"view upstream error"}),Kt()})});Xt.on("error",k=>{M({v:It,ch:"control",type:"http_end",reqId:P,error:k?.message||"view request failed"}),Kt()}),bt&&Xt.write(Buffer.from(bt,"base64")),Xt.end()})}function rt(){if(g)return;h=new Yu(t,{headers:{authorization:`Bearer ${e}`}}),h.on("open",async()=>{let R=null;try{R=await s?.()||null}catch{R=null}M({v:It,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:ui,providers:WR(),...R?{agents:R}:{}})}),h.on("message",R=>{let _;try{_=JSON.parse(R)}catch{return}if(_.ch==="ctl"&&_.type==="hello_ok"){w=!0,b=Yy,S=null,i.log(`[plane-link] connected to plane as ${n}`),_.minNodeVersion&&ui&&HR(ui,_.minNodeVersion)&&i.error(`[plane-link] this node is bostrat-node ${ui}; the plane asks for \u2265${_.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),z&&clearInterval(z),z=setInterval(()=>M({v:It,ch:"ctl",type:"ping"}),JR),z.unref?.(),st().catch(P=>i.error("[plane-link] backfill failed:",P?.message||P));return}if(_.ch==="events"&&_.type==="ack"&&Array.isArray(_.ids)){for(let P of _.ids){let et=m.get(P);et&&(m.delete(P),(d[et.trackId]??0)<et.seq&&(d[et.trackId]=et.seq))}T();return}if(_.ch==="control"&&_.type==="http_open"){v(_).catch(P=>M({v:It,ch:"control",type:"http_end",reqId:_.reqId,error:P?.message||"tunnel error"}));return}if(_.ch==="control"&&_.type==="http_cancel"){p.get(_.reqId)?.abort();return}if(_.ch==="attach"&&_.type==="open"){let P=new Yu(c+_.path,Array.isArray(_.protocols)?_.protocols:[],_.host?{headers:{host:_.host}}:void 0);l.set(_.streamId,P),u.set(_.streamId,[]),P.on("open",()=>{let wt=u.get(_.streamId)||[];u.delete(_.streamId);for(let{buf:bt,bin:$t}of wt)P.send(bt,{binary:$t})}),P.on("message",(wt,bt)=>{let $t=bt?wt:Buffer.from(String(wt));M({v:It,ch:"attach",type:"data",streamId:_.streamId,data:$t.toString("base64"),...bt?{bin:!0}:{}})});let et=()=>{u.delete(_.streamId),l.delete(_.streamId)&&M({v:It,ch:"attach",type:"close",streamId:_.streamId})};P.on("close",et),P.on("error",et);return}if(_.ch==="attach"&&_.type==="data"){let P=l.get(_.streamId);if(!P)return;let et=Buffer.from(_.data,"base64"),wt=_.bin===!0,bt=u.get(_.streamId);bt?bt.push({buf:et,bin:wt}):P.send(et,{binary:wt});return}if(_.ch==="attach"&&_.type==="close"){let P=l.get(_.streamId);l.delete(_.streamId),u.delete(_.streamId),P?.close();return}});let I=()=>{w&&i.log("[plane-link] disconnected from plane"),w=!1,z&&(clearInterval(z),z=null),m.clear();for(let P of l.values())P.close();l.clear(),u.clear();for(let P of p.values())P.abort();if(p.clear(),g||A)return;S==null&&(S=Date.now());let R=Date.now()-S<KR,_=R?GR+Math.random()*zR:b;A=setTimeout(()=>{A=null,rt()},_),A.unref?.(),R||(b=Math.min(b*2,qR))};h.on("close",I),h.on("error",I)}return zt.on("event",y),F().then(rt),{stop:()=>{g=!0,zt.off("event",y),A&&clearTimeout(A),C&&clearTimeout(C),z&&clearInterval(z),M({v:It,ch:"ctl",type:"bye",reason:"shutdown"}),h?.close()},status:()=>({connected:w,nodeId:n,cursor:{...d},inflight:m.size})}}function Qu(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 tb=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),eb=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Zu=t=>Math.round(t*1e4)/1e4;async function nb(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=tb(),u=0,f=[];for(let d of t||[]){let m=await L(d),h=ue(m,{boxLabels:Qu(d)});if(h.turns===0){d.archivedAt?u++:f.push(d.name||d.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.unpricedTurns||0;for(let w of h.unpricedModels||[])c.add(w);eb(l,h.totalTokens);for(let[w,g]of Object.entries(h.byModel))n[w]||={usd:0,events:0,tokens:tb()},n[w].usd=Zu(n[w].usd+g.usd),n[w].events+=g.events,eb(n[w].tokens,g.tokens);for(let[w,g]of Object.entries(h.byProvider))r[w]||={usd:0,events:0},r[w].usd=Zu(r[w].usd+g.usd),r[w].events+=g.events;e.push({id:d.id,name:d.name,usd:h.totalUsd,rollup:h})}return e.sort((d,m)=>m.usd-d.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Zu(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 ps=t=>`$${t.toFixed(4)}`,Jr=(t,e)=>String(t).padEnd(e);function rb(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 ${ps(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(` ${Jr(a,26)} ${Jr(ps(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(` ${Jr(a.name||a.id,30)} ${ps(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Jr(c.label,40)} ${Jr(ps(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))}${Jr(ps(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
389
- `)}import t$ from"node:fs/promises";import e$ from"node:path";var fi=t=>t.payload?.ts??t.ts,XR=12e4,yn=t=>Math.round(t*1e4)/1e4,YR=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function ZR(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>fi(S)>=e,a=lr([bo(n?.name),bo(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 A=lr(S.payload.task);A&&i.set(S.boxId,A)}let c=new Map,l=S=>{let A=S||"unattributed";return c.has(A)||c.set(A,{segments:[u(null)],lastTs:null,provider:null}),c.get(A)},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,A)=>fi(S)-fi(A)),d=0,m=0,h=0,w={turns:0};for(let S of f){let A=l(S.boxId),C=A.segments[A.segments.length-1],z=fi(S);switch(A.lastTs!=null&&z>A.lastTs&&s(S)&&(C.activeMs+=Math.min(z-A.lastTs,XR)),A.lastTs=z,S.provider&&(A.provider=S.provider),S.type){case"turn.user":{let O=S.payload?.text;if(S.source==="hook"||!String(O||"").trim())break;let F=u(lr(O));A.segments.push(F),s(S)&&(F.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let O=S.payload?.tool;O&&(C.tools[O]=(C.tools[O]||0)+1),s(S)&&(C.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let O=S.payload||{},F={input:O.tokens?.input||0,output:O.tokens?.output||0,cacheRead:O.tokens?.cacheRead||0,cacheWrite:O.tokens?.cacheWrite??O.tokens?.cacheCreation??0},T=Number(O.usd)||0;!T&&O.tokens&&(T=le(O.model,F)),!T&&O.tokens&&!ea(O.model)&&(w.turns+=1),C.usd+=T,C.tokens.input+=F.input,C.tokens.output+=F.output,C.tokens.cacheRead+=F.cacheRead,C.tokens.cacheWrite+=F.cacheWrite;let M=O.model||"unknown";C.models[M]=(C.models[M]||0)+T,S.provider&&(C.provider=S.provider);break}case"handoff.done":s(S)&&(d+=1);break;case"subtask.merged":s(S)&&(m+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let g=new Map,b=!1;for(let[S,A]of c)for(let C of A.segments){if(!(C.usd>0||C.activeMs>0||C.turns>0||C.toolCalls>0))continue;let O=C.textCat?.category??i.get(S)?.category??If(C.tools)?.category??a?.category??"uncategorized";g.has(O)||g.set(O,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let F=g.get(O);F.usd+=C.usd,F.activeMs+=C.activeMs,F.turns+=C.turns,F.toolCalls+=C.toolCalls;for(let[M,W]of Object.entries(C.models))F.models[M]=(F.models[M]||0)+W;let T=C.provider||A.provider;T&&(F.providers[T]=(F.providers[T]||0)+C.usd),T==="cursor"&&C.usd===0&&(b=!0)}return{perCategory:g,handoffs:d,merged:m,failed:h,sawCursorActivity:b,unpricedTurns:w.turns}}function sb(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:g,events:b}=w;if(!b?.length){a+=1;continue}let{perCategory:S,handoffs:A,merged:C,failed:z,sawCursorActivity:O,unpricedTurns:F}=ZR(w,{since:r});O&&(i=!0),c+=F;let T={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:A,subtasksMerged:C,subtasksFailed:z};for(let[M,W]of S){T.usd+=W.usd,T.activeMs+=W.activeMs,T.turns+=W.turns,T.categories[M]={usd:yn(W.usd),activeMs:W.activeMs};for(let[p,v]of Object.entries(W.models))T.models[p]=yn((T.models[p]||0)+v);for(let[p,v]of Object.entries(W.providers))T.providers[p]=yn((T.providers[p]||0)+v);o.has(M)||o.set(M,YR());let st=o.get(M);st.usd+=W.usd,st.activeMs+=W.activeMs,st.turns+=W.turns,st.toolCalls+=W.toolCalls;for(let[p,v]of Object.entries(W.models))st.models[p]=(st.models[p]||0)+v;for(let[p,v]of Object.entries(W.providers))st.providers[p]=(st.providers[p]||0)+v;let y=st.byTrack.get(g?.id)||{usd:0,activeMs:0};y.usd+=W.usd,y.activeMs+=W.activeMs,st.byTrack.set(g?.id,y)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=yn(T.usd),T.dominantCategory=QR(T.categories),s.push(T))}let l=[...o.values()].reduce((w,g)=>w+g.usd,0),u=[...o.values()].reduce((w,g)=>w+g.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,m=new Map((t||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),h=[...o.entries()].map(([w,g])=>({category:w,sharePct:d>0?Math.round((f==="usd"?g.usd:g.activeMs)/d*100):0,shareBasis:f,usd:yn(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:ob(g.models),providers:ob(g.providers),topWorkstreams:[...g.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:m.get(b),usd:yn(S.usd),activeMs:S.activeMs}))})).sort((w,g)=>g.usd-w.usd||g.activeMs-w.activeMs);return s.sort((w,g)=>g.usd-w.usd||g.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:yn(l),activeMs:u,turns:[...o.values()].reduce((w,g)=>w+g.turns,0),bullets:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var QR=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},ob=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,yn(n)]));var Xr={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function n$(t){let e=String(t||"7d");return e in Xr?{key:e,ms:Xr[e]}:{key:"7d",ms:Xr["7d"]}}function r$(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=Da(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 o$(t){if(!t?.cwd)return null;try{let n=(await t$.readFile(e$.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 ab(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}:n$(e),f=[];for(let d of t||[]){let m=await L(d),h=r$(d,m);i&&c<o&&(h=h.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:h,brief:await o$(d)})}return{window:l,...sb(f,{windowMs:u,now:c})}}var bn=36e5,ub=24*bn,s$=400,a$=12,Yr=t=>Math.round(t*1e4)/1e4;function i$({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 Xr?String(t||"7d"):"7d",i=Xr[a];return{key:a,since:i==null?null:r-i,until:r}}var c$=(t,e)=>t!=null&&e-t<=48*bn?bn:ub;function ib(t,e){if(e===bn)return Math.floor(t/bn)*bn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function l$(t,e){if(e===bn)return t+bn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var cb=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),lb=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},u$=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Yr(n)]));async function db(t,e={}){let n=e.now??Date.now(),r=i$({...e,now:n}),o=r.since==null?ub:c$(r.since,r.until),s=T=>(r.since==null||T>=r.since)&&T<r.until,a=r.since!=null?r.until-r.since:null,i=T=>a!=null&&T>=r.since-a&&T<r.since,c={},l={},u=cb(),f=0,d=0,m=0,h=0,w=0,g=new Set,b=new Map,S=[],A=[],C=[],z=null;for(let T of t||[]){let W=(await L(T)).filter(p=>p.type==="cost.recorded");if(!W.length){T.archivedAt||C.push(T.name||T.id);continue}let st=[];for(let p of W){let v=p.ts??0;s(v)?st.push(p):i(v)&&(w+=ra(p).usd)}if(!st.length)continue;let y=ue(st,{boxLabels:Qu(T)});f+=y.totalUsd,d+=y.turns,m+=y.repricedTurns||0,h+=y.unpricedTurns||0;for(let p of y.unpricedModels||[])g.add(p);lb(u,y.totalTokens);for(let[p,v]of Object.entries(y.byModel))c[p]||={usd:0,events:0,tokens:cb()},c[p].usd=Yr(c[p].usd+v.usd),c[p].events+=v.events,lb(c[p].tokens,v.tokens);for(let[p,v]of Object.entries(y.byProvider))l[p]||={usd:0,events:0},l[p].usd=Yr(l[p].usd+v.usd),l[p].events+=v.events;for(let p of st){let{usd:v}=ra(p);if(!v)continue;let E=ib(p.ts??0,o),rt=b.get(E)??{t:E,total:0,byProvider:{}};rt.total+=v;let I=p.provider||"unknown";rt.byProvider[I]=(rt.byProvider[I]||0)+v,b.set(E,rt),(z==null||E<z)&&(z=E)}S.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:y.totalUsd,turns:y.turns,providers:Object.keys(y.byProvider)});for(let[p,v]of Object.entries(y.byBox))A.push({trackId:T.id,trackName:T.name||T.id,boxId:p,...v})}let O=r.since!=null?ib(r.since,o):z,F=[];for(let T=O,M=0;T!=null&&T<r.until&&M<s$;T=l$(T,o),M++){let W=b.get(T);F.push(W?{t:T,total:Yr(W.total),byProvider:u$(W.byProvider)}:{t:T,total:0,byProvider:{}})}return S.sort((T,M)=>M.usd-T.usd),A.sort((T,M)=>M.usd-T.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:F,fleet:{totalUsd:Yr(f),turns:d,repricedTurns:m,unpricedTurns:h,unpricedModels:[...g],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:C},prior:a!=null?{totalUsd:Yr(w),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:A.slice(0,a$)}}import to from"node:fs/promises";import d$ from"node:fs";import dt from"node:path";import f$ from"node:os";import{execFile as p$}from"node:child_process";import{promisify as m$}from"node:util";var h$=m$(p$),fb=process.env.BOSTRAT_STATE_DIR||dt.join(process.env.HOME||f$.tmpdir(),".bostrat"),td=!1;async function Zr(t,e){let n=await to.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await to.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await to.mkdir(dt.dirname(e),{recursive:!0}),await to.copyFile(t,e),!0)}var w$=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Qr(t,e){let n=0,r=await to.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let o of r){if(w$.has(o.name))continue;let s=dt.join(t,o.name);o.isDirectory()?n+=await Qr(s,dt.join(e,o.name)):o.isFile()&&(n+=await Zr(s,dt.join(e,o.name))?1:0)}return n}async function pb({stateDir:t=fb,dest:e=process.env.BOSTRAT_SYNC_DIR||`${fb}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await to.mkdir(e,{recursive:!0});let o=0;o+=await Zr(dt.join(t,"tracks.json"),dt.join(e,"tracks.json"))?1:0,o+=await Zr(dt.join(t,"mocks.json"),dt.join(e,"mocks.json"))?1:0,o+=await Zr(dt.join(t,"flows.json"),dt.join(e,"flows.json"))?1:0,o+=await Zr(dt.join(t,"runbooks.json"),dt.join(e,"runbooks.json"))?1:0,o+=await Qr(dt.join(t,"runbooks"),dt.join(e,"runbooks")),o+=await Qr(dt.join(t,"mock-archive"),dt.join(e,"mock-archive")),o+=await Zr(dt.join(t,"events.jsonl"),dt.join(e,"events.jsonl"))?1:0,o+=await Qr(dt.join(t,"events"),dt.join(e,"events")),o+=await Qr(dt.join(t,"memory"),dt.join(e,"memory"));for(let l of r||[])!l?.cwd||!l?.id||(o+=await Qr(dt.join(l.cwd,".bostrat"),dt.join(e,"workspaces",l.id,".bostrat")));let s=(...l)=>h$("git",["-C",e,...l]);d$.existsSync(dt.join(e,".git"))||(await s("init","-q"),await s("config","user.email","agent@bostrat.local"),await s("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await s("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Di(e).catch(()=>{}));let a=!1,{stdout:i}=await s("status","--porcelain");i.trim()&&(await s("add","-A"),await s("commit","-q","-m",`state sync ${new Date().toISOString()}`),a=!0);let c=!1;if(n&&(a||td))try{await s("push","-q",n,"HEAD:refs/heads/main"),c=!0,td=!1}catch{td=!0}return{staged:o,committed:a,pushed:c}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as g$}from"node:child_process";import{promisify as y$}from"node:util";var b$=y$(g$),k$=12e4,x$=6e4;function S$(t){return`refs/bostrat/wip/${t}`}function wb(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function v$(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:k$}var pi=(t,e,n=15e3)=>b$("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),mb=new Map,hb=new Set;async function T$(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await pi(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await pi(r,["remote","get-url","origin"])}catch{return hb.has(r)||(hb.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 zo(r,"bostrat: wip snapshot"),{stdout:s}=await pi(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(mb.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=S$(n);return await pi(r,["push","--force","origin",`${o}:${i}`],x$),mb.set(r,a),await $({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
390
- `)[0]}`),{pushed:!1,reason:"push-failed"}}}function gb({listTracks:t,sweepMs:e=v$(),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 T$(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 A$}from"node:child_process";import{promisify as E$}from"node:util";import{randomUUID as _$}from"node:crypto";import I$ from"node:fs";import R$ from"node:os";import $$ from"node:path";var xn=E$(A$),mi=t=>new Promise(e=>setTimeout(e,t)),Sn=Vi,ed=process.env.BOSTRAT_USAGE_PROBE_CWD||$$.join(R$.homedir(),".bostrat","usage-probe"),O$=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,M$=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,C$=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,yb=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,kn=null,ms=null,hs=null;async function N$(){try{return await xn(N,B("has-session","-t",Sn)),!0}catch{return!1}}function P$(){kn&&clearTimeout(kn),kn=setTimeout(()=>B$().catch(()=>{}),O$),kn.unref&&kn.unref()}async function B$(){kn&&(clearTimeout(kn),kn=null);try{await xn(N,B("kill-session","-t",Sn))}catch{}}async function D$(){let t=Date.now()+C$;for(;;){let e=await nd().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await mi(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await mi(500)}}async function j$(){if(await N$())return;if(!await Je())throw new Error("host tmux server not running \u2014 start globo-tmux.service");I$.mkdirSync(ed,{recursive:!0}),await Ge(ed);let t=_$();await xn(N,B("new-session","-d","-s",Sn,"-c",ed,"-x","200","-y","60"));let e=`${zi} --session-id ${t}`;await xn(N,B("send-keys","-t",Sn,e,"Enter")),await D$()}async function nd(){let{stdout:t}=await xn(N,B("capture-pane","-t",Sn,"-p"));return t}async function L$(){await j$(),await xn(N,B("send-keys","-t",Sn,"-l","/usage")),await xn(N,B("send-keys","-t",Sn,"Enter"));let t=Date.now()+M$,e="";for(;await mi(400),e=await nd(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await mi(900),e=await nd(),await xn(N,B("send-keys","-t",Sn,"Escape")).catch(()=>{}),P$(),e}function F$(t){let e=String(t||"").split(`
391
- `).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 U$(t){let e=String(t||"").split(`
392
- `).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 bb({fresh:t=!1}={}){return!t&&ms&&Date.now()-ms.at<yb?ms.data:hs||(hs=(async()=>{try{let e=await L$(),n=F$(e),r={...n,contributors:U$(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()-(yb-3e3),data:n},n}finally{hs=null}})(),hs)}var H$=262144;function W$(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 kb(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:W$(t.resets_in_seconds)}:null}function q$(t){let e=String(t||"").split(`
393
- `),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:kb(i.primary),week:kb(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 rd(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Fn(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 Mt(n.file,H$),o=q$(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 xb(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 hi({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 rd(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await rd(null))}return n.has("cursor")&&r.push(await hi({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function hi(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?rd(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await bb({fresh:e})}}function K$(t=process.env){return!!(Gt(t)&&t.BOSTRAT_PLANE_TOKEN)}var G$=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),wi=(t,e)=>Object.assign(new Error(t),{status:e});async function Sb(t,e){if(!K$(e))throw wi("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Gt(e)}${t}`,{headers:G$(e)})}catch(o){throw wi(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw wi(r?.error||`plane replied ${n.status}`,n.status);return r}async function vb(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await Sb(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function Tb(t,{env:e=process.env}={}){if(!t)throw wi("message id required",400);return(await Sb(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function Ab(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 z$(t){let[e,n]=t.split("/"),r=Ab(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 V$(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(z$).filter(Boolean)}function J$(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function Eb(t){let e=V$(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:J$,allowed(r){if(n)return!0;let o=Ab((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}import _b from"node:crypto";import gi from"node:fs";import X$ from"node:path";function Ib(t=process.env,{stateDir:e=Vy,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=X$.join(e,"api-token");try{let a=gi.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=_b.randomBytes(32).toString("base64url");try{gi.mkdirSync(e,{recursive:!0}),gi.writeFileSync(o,s+`
394
- `,{mode:384}),gi.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function ws(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 Rb(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:_b.timingSafeEqual(n,r)}function $b(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 Y$=[/^\/api\/github\/token\b/],Z$=t=>Y$.some(e=>e.test(t));function Ob({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=$b(o);return Rb(c,t)?a():Z$(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||ws(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 Mb(t,e,{enforce:n=!0}={}){return!n||Rb($b(t),e)?!0:ws(t)}import Q$ from"node:http";import tO from"node:net";function Cb(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 Nb(t){let e=Cb(t.headers.host);return e?{slug:e,view:$c(e)}:null}var eO=t=>`<!doctype html><meta charset="utf-8">
384
+ _${o} archived_`:"")}async function OR(t,e){let n=await Bn();if(!n)return ut("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 ut("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?(Yn(nb(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),ut(`Starting *${Y(u)}* on ${Y(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:RR(u,i)}}case"archive":{if(!a.length)return ut("Usage: `/bostrat archive <track>`");let{match:i,live:c}=zu(await r.listTracks(),a.join(" "));return i?(Yn(ER(t,n,{track:i,responseUrl:e.response_url})),ut(`Archiving *${Y(i.name)}*\u2026`)):Ju(c,a.join(" "))}case"handoff":{let i=r.knownAgents??AR,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return ut(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=zu(await r.listTracks(),l);return u?(Yn(_R(t,n,{track:u,agent:c,responseUrl:e.response_url})),ut(`Handing *${Y(u.name)}* to ${Y(c)}\u2026`)):Ju(f,l)}case"stop":{if(!a.length)return ut("Usage: `/bostrat stop <track>`");let{match:i,live:c}=zu(await r.listTracks(),a.join(" "));return i?(Yn(IR(t,n,{track:i,responseUrl:e.response_url})),ut(`Stopping *${Y(i.name)}*\u2026`)):Ju(c,a.join(" "))}case"list":return ut(await $R(r));default:return ut(TR)}}async function CR(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Qu(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 We(),await Vr(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(l||"question")}~
385
+ \u2713 *${Y(a)}*`}}]}).catch(()=>{})}}catch(c){await jt(n.response_url,ut(`Couldn't answer: ${c?.message||c}`))}}async function MR(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,Ba(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 jt(n.response_url,ut(`Couldn't act on the offer: ${c?.message||c}`))}}async function NR(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await Bn();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 jt(e.response_url,{...ut(`Starting *${Y(s)}*\u2026`),replace_original:!0}),Yn(nb(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"?Yn(CR(t,r,e,o)):n.action_id==="bostrat-ship"&&Yn(MR(t,r,e,o))}async function PR(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 SR(e.channel,e.thread_ts);if(!n)return;let r=await Bn();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await Zn(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function BR(t){return async e=>{if(e.type==="slash_commands")return OR(t,e.payload);if(e.type==="interactive"){await NR(t,e.payload);return}if(e.type==="events_api"){await PR(t,e.payload?.event);return}}}var DR=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 jR(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:us(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 LR(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await Vr(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 Zn(t,e,r.question||"The lead needs an answer",jR(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await We()}async function FR(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 We(),await Vr(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(s||"question")}~
386
+ \u2713 _answered_`}}]})}async function UR(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 Zn(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Y(us(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await We()}async function HR(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await We();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await Vr(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function WR(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??qR)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Xy(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 Zn(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 qR(t,e){let n=await en(t);if(!n)return null;let r=_e(n,e);if(!r)return null;let o=await ls.readFile(Ht(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function KR(t,e){if(!DR.has(e.type))return;let n=await Qu(e.trackId);if(!n)return;let r=await Bn();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 We(),await Yu(r,n);return;case"turn.agent":{if(!o.text)return;let s=vR(o.text);if(!s)return;await Zn(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Y(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${ya(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return LR(r,n,e);case"prompt.answered":return FR(r,n,e);case"pr.offered":return UR(r,n,e);case"pr.offer.answered":return HR(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 Zn(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 We(),await Yu(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Zn(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)}
387
+ _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return WR(r,n,e,t.deps)}}function rb(t){let e={deps:t};Jt.on("event",s=>{KR(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Bn()||(r&&clearInterval(r),r=null,n=Yy({getToken:async()=>(await Bn())?.appToken,onEnvelope:BR(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import GR from"node:fs/promises";function VR(t){let e=new Map;for(let n of String(t||"").split(`
388
+ `)){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 zR(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 JR(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Ae(e.cwd,e.sessionId):e?.cwd?ic(e.cwd):null}async function ob(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 XR(r,o,t,i);break;default:break}return a}async function XR(t,e,n,r){let o=[],s=JR(n,e);if(s)try{o=VR(await GR.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=zR(await D(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:le(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 QR from"node:fs";import td from"node:fs/promises";import t$ from"node:http";import zr from"node:path";import li from"node:os";import{createRequire as e$}from"node:module";import ed from"ws";import YR from"node:path";import ZR from"node:os";var sb=process.env.BOSTRAT_STATE_DIR||YR.join(process.env.HOME||ZR.tmpdir(),".bostrat");function ab(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 ib(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 ci=(()=>{try{return e$(import.meta.url)("../package.json").version||null}catch{return null}})(),n$=process.env.BOSTRAT_STATE_DIR||zr.join(process.env.HOME||li.tmpdir(),".bostrat");function r$(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 o$({home:t=process.env.HOME||li.homedir(),env:e=process.env}={}){let n=r=>{try{return QR.statSync(r),!0}catch{return!1}};return{claude:n(zr.join(t,".claude",".credentials.json")),codex:n(zr.join(e.CODEX_HOME||zr.join(t,".codex"),"auth.json"))}}var _t=1,cb=1e3,s$=3e4,a$=6e4,i$=1500,c$=500,lb=100,l$=2e3,u$=25e3;function ub({url:t,token:e,nodeId:n=li.hostname(),name:r=li.hostname(),listTracks:o,detectAgents:s=null,selfUrl:a=`http://127.0.0.1:${process.env.PORT||3362}`,log:i=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=ib(t);let c=a.replace(/^http/,"ws"),l=new Map,u=new Map,f=zr.join(n$,"plane-link","cursor.json"),d={},p=new Map,m=null,g=!1,y=!1,b=cb,S=null,E=null,M=null,z=null,$=!1;async function j(){try{d=JSON.parse(await td.readFile(f,"utf8"))}catch{d={}}}function v(){$=!0,!M&&(M=setTimeout(async()=>{if(M=null,!!$){$=!1;try{await td.mkdir(zr.dirname(f),{recursive:!0}),await td.writeFile(f,JSON.stringify(d))}catch(O){i.error("[plane-link] cursor flush failed:",O?.message||O)}}},l$),M.unref?.())}function C(O){return!m||m.readyState!==ed.OPEN?!1:(m.send(JSON.stringify(O)),!0)}function H(O){for(let{trackId:I,event:A}of O)p.set(A.id,{trackId:I,seq:A.seq});return C({v:_t,ch:"events",type:"append",events:O})}async function nt(){let O;try{O=await o()}catch(I){i.error("[plane-link] backfill listTracks failed:",I?.message||I);return}C({v:_t,ch:"ctl",type:"tracks",ids:O.map(I=>I.id).filter(I=>typeof I=="string")});for(let I of O){if(!g)return;let A;try{A=await D(I)}catch{continue}let K=d[I.id]??0,q=A.filter(dt=>dt.seq>K&&typeof dt.id=="string");for(let dt=0;dt<q.length;dt+=lb){let yt=q.slice(dt,dt+lb).map(Ot=>({trackId:I.id,event:Ot}));if(!H(yt))return}}}function ot(O){!g||!O?.trackId||typeof O.id!="string"||H([{trackId:O.trackId,event:O}])}let w=new Map;async function h(O){let{reqId:I,method:A,path:K,headers:q={},body:dt}=O,yt=Object.fromEntries(Object.entries(q).filter(([,Vt])=>Vt!=null)),Ot=new AbortController;w.set(I,Ot);try{if(O.host){await T(O,yt,Ot);return}let Vt=await fetch(a+K,{method:A,headers:yt,body:dt?Buffer.from(dt,"base64"):void 0,signal:Ot.signal}),Zt=Object.fromEntries(Vt.headers);for(let k of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Zt[k];if(Zt["content-type"]||(Zt["content-type"]="application/octet-stream"),C({v:_t,ch:"control",type:"http_head",reqId:I,status:Vt.status,headers:Zt}),Vt.body){for await(let k of Vt.body)if(!C({v:_t,ch:"control",type:"http_data",reqId:I,data:Buffer.from(k).toString("base64")}))break}C({v:_t,ch:"control",type:"http_end",reqId:I,error:null})}finally{w.delete(I)}}function T(O,I,A){let{reqId:K,method:q,path:dt,body:yt}=O,Ot=new URL(a);return new Promise(Vt=>{let Zt=t$.request({host:Ot.hostname,port:Ot.port,method:q,path:dt,headers:{...I,host:O.host},signal:A.signal},k=>{let{connection:G,"keep-alive":Qn,"transfer-encoding":vs,upgrade:tr,...$i}=k.headers;C({v:_t,ch:"control",type:"http_head",reqId:K,status:k.statusCode||502,headers:$i}),k.on("data",zk=>{C({v:_t,ch:"control",type:"http_data",reqId:K,data:Buffer.from(zk).toString("base64")})||k.destroy()}),k.on("end",()=>{C({v:_t,ch:"control",type:"http_end",reqId:K,error:null}),Vt()}),k.on("error",()=>{C({v:_t,ch:"control",type:"http_end",reqId:K,error:"view upstream error"}),Vt()})});Zt.on("error",k=>{C({v:_t,ch:"control",type:"http_end",reqId:K,error:k?.message||"view request failed"}),Vt()}),yt&&Zt.write(Buffer.from(yt,"base64")),Zt.end()})}function _(){if(y)return;m=new ed(t,{headers:{authorization:`Bearer ${e}`}}),m.on("open",async()=>{let I=null;try{I=await s?.()||null}catch{I=null}C({v:_t,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:ci,providers:o$(),...I?{agents:I}:{}})}),m.on("message",I=>{let A;try{A=JSON.parse(I)}catch{return}if(A.ch==="ctl"&&A.type==="hello_ok"){g=!0,b=cb,S=null,i.log(`[plane-link] connected to plane as ${n}`),A.minNodeVersion&&ci&&r$(ci,A.minNodeVersion)&&i.error(`[plane-link] this node is bostrat-node ${ci}; the plane asks for \u2265${A.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),z&&clearInterval(z),z=setInterval(()=>C({v:_t,ch:"ctl",type:"ping"}),u$),z.unref?.(),nt().catch(K=>i.error("[plane-link] backfill failed:",K?.message||K));return}if(A.ch==="events"&&A.type==="ack"&&Array.isArray(A.ids)){for(let K of A.ids){let q=p.get(K);q&&(p.delete(K),(d[q.trackId]??0)<q.seq&&(d[q.trackId]=q.seq))}v();return}if(A.ch==="control"&&A.type==="http_open"){h(A).catch(K=>C({v:_t,ch:"control",type:"http_end",reqId:A.reqId,error:K?.message||"tunnel error"}));return}if(A.ch==="control"&&A.type==="http_cancel"){w.get(A.reqId)?.abort();return}if(A.ch==="attach"&&A.type==="open"){let K=new ed(c+A.path,Array.isArray(A.protocols)?A.protocols:[],A.host?{headers:{host:A.host}}:void 0);l.set(A.streamId,K),u.set(A.streamId,[]),K.on("open",()=>{let dt=u.get(A.streamId)||[];u.delete(A.streamId);for(let{buf:yt,bin:Ot}of dt)K.send(yt,{binary:Ot})}),K.on("message",(dt,yt)=>{let Ot=yt?dt:Buffer.from(String(dt));C({v:_t,ch:"attach",type:"data",streamId:A.streamId,data:Ot.toString("base64"),...yt?{bin:!0}:{}})});let q=()=>{u.delete(A.streamId),l.delete(A.streamId)&&C({v:_t,ch:"attach",type:"close",streamId:A.streamId})};K.on("close",q),K.on("error",q);return}if(A.ch==="attach"&&A.type==="data"){let K=l.get(A.streamId);if(!K)return;let q=Buffer.from(A.data,"base64"),dt=A.bin===!0,yt=u.get(A.streamId);yt?yt.push({buf:q,bin:dt}):K.send(q,{binary:dt});return}if(A.ch==="attach"&&A.type==="close"){let K=l.get(A.streamId);l.delete(A.streamId),u.delete(A.streamId),K?.close();return}});let O=()=>{g&&i.log("[plane-link] disconnected from plane"),g=!1,z&&(clearInterval(z),z=null),p.clear();for(let K of l.values())K.close();l.clear(),u.clear();for(let K of w.values())K.abort();if(w.clear(),y||E)return;S==null&&(S=Date.now());let I=Date.now()-S<a$,A=I?i$+Math.random()*c$:b;E=setTimeout(()=>{E=null,_()},A),E.unref?.(),I||(b=Math.min(b*2,s$))};m.on("close",O),m.on("error",O)}return Jt.on("event",ot),j().then(_),{stop:()=>{y=!0,Jt.off("event",ot),E&&clearTimeout(E),M&&clearTimeout(M),z&&clearInterval(z),C({v:_t,ch:"ctl",type:"bye",reason:"shutdown"}),m?.close()},status:()=>({connected:g,nodeId:n,cursor:{...d},inflight:p.size})}}function rd(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 db=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),fb=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},nd=t=>Math.round(t*1e4)/1e4;async function pb(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=db(),u=0,f=[];for(let d of t||[]){let p=await D(d),m=ue(p,{boxLabels:rd(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 g of m.unpricedModels||[])c.add(g);fb(l,m.totalTokens);for(let[g,y]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:db()},n[g].usd=nd(n[g].usd+y.usd),n[g].events+=y.events,fb(n[g].tokens,y.tokens);for(let[g,y]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=nd(r[g].usd+y.usd),r[g].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:nd(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 ds=t=>`$${t.toFixed(4)}`,Jr=(t,e)=>String(t).padEnd(e);function mb(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 ${ds(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(` ${Jr(a,26)} ${Jr(ds(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(` ${Jr(a.name||a.id,30)} ${ds(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Jr(c.label,40)} ${Jr(ds(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))}${Jr(ds(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
389
+ `)}import h$ from"node:fs/promises";import w$ from"node:path";var ui=t=>t.payload?.ts??t.ts,d$=12e4,bn=t=>Math.round(t*1e4)/1e4,f$=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function p$(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>ui(S)>=e,a=dr([ko(n?.name),ko(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=dr(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)=>ui(S)-ui(E)),d=0,p=0,m=0,g={turns:0};for(let S of f){let E=l(S.boxId),M=E.segments[E.segments.length-1],z=ui(S);switch(E.lastTs!=null&&z>E.lastTs&&s(S)&&(M.activeMs+=Math.min(z-E.lastTs,d$)),E.lastTs=z,S.provider&&(E.provider=S.provider),S.type){case"turn.user":{let $=S.payload?.text;if(S.source==="hook"||!String($||"").trim())break;let j=u(dr($));E.segments.push(j),s(S)&&(j.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let $=S.payload?.tool;$&&(M.tools[$]=(M.tools[$]||0)+1),s(S)&&(M.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let $=S.payload||{},j={input:$.tokens?.input||0,output:$.tokens?.output||0,cacheRead:$.tokens?.cacheRead||0,cacheWrite:$.tokens?.cacheWrite??$.tokens?.cacheCreation??0},v=Number($.usd)||0;!v&&$.tokens&&(v=le($.model,j)),!v&&$.tokens&&!na($.model)&&(g.turns+=1),M.usd+=v,M.tokens.input+=j.input,M.tokens.output+=j.output,M.tokens.cacheRead+=j.cacheRead,M.tokens.cacheWrite+=j.cacheWrite;let C=$.model||"unknown";M.models[C]=(M.models[C]||0)+v,S.provider&&(M.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 M of E.segments){if(!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let $=M.textCat?.category??i.get(S)?.category??Gf(M.tools)?.category??a?.category??"uncategorized";y.has($)||y.set($,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let j=y.get($);j.usd+=M.usd,j.activeMs+=M.activeMs,j.turns+=M.turns,j.toolCalls+=M.toolCalls;for(let[C,H]of Object.entries(M.models))j.models[C]=(j.models[C]||0)+H;let v=M.provider||E.provider;v&&(j.providers[v]=(j.providers[v]||0)+M.usd),v==="cursor"&&M.usd===0&&(b=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:b,unpricedTurns:g.turns}}function wb(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let g of t||[]){let{track:y,events:b}=g;if(!b?.length){a+=1;continue}let{perCategory:S,handoffs:E,merged:M,failed:z,sawCursorActivity:$,unpricedTurns:j}=p$(g,{since:r});$&&(i=!0),c+=j;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:M,subtasksFailed:z};for(let[C,H]of S){v.usd+=H.usd,v.activeMs+=H.activeMs,v.turns+=H.turns,v.categories[C]={usd:bn(H.usd),activeMs:H.activeMs};for(let[w,h]of Object.entries(H.models))v.models[w]=bn((v.models[w]||0)+h);for(let[w,h]of Object.entries(H.providers))v.providers[w]=bn((v.providers[w]||0)+h);o.has(C)||o.set(C,f$());let nt=o.get(C);nt.usd+=H.usd,nt.activeMs+=H.activeMs,nt.turns+=H.turns,nt.toolCalls+=H.toolCalls;for(let[w,h]of Object.entries(H.models))nt.models[w]=(nt.models[w]||0)+h;for(let[w,h]of Object.entries(H.providers))nt.providers[w]=(nt.providers[w]||0)+h;let ot=nt.byTrack.get(y?.id)||{usd:0,activeMs:0};ot.usd+=H.usd,ot.activeMs+=H.activeMs,nt.byTrack.set(y?.id,ot)}(v.usd>0||v.activeMs>0||v.turns>0)&&(v.usd=bn(v.usd),v.dominantCategory=m$(v.categories),s.push(v))}let l=[...o.values()].reduce((g,y)=>g+y.usd,0),u=[...o.values()].reduce((g,y)=>g+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((t||[]).map(g=>[g.track?.id,g.track?.name||g.track?.id])),m=[...o.entries()].map(([g,y])=>({category:g,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:bn(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:hb(y.models),providers:hb(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:bn(S.usd),activeMs:S.activeMs}))})).sort((g,y)=>y.usd-g.usd||y.activeMs-g.activeMs);return s.sort((g,y)=>y.usd-g.usd||y.activeMs-g.activeMs),{since:r,capturedAt:n,totalUsd:bn(l),activeMs:u,turns:[...o.values()].reduce((g,y)=>g+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var m$=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},hb=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,bn(n)]));var Xr={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function g$(t){let e=String(t||"7d");return e in Xr?{key:e,ms:Xr[e]}:{key:"7d",ms:Xr["7d"]}}function y$(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=Pa(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 b$(t){if(!t?.cwd)return null;try{let n=(await h$.readFile(w$.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 gb(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}:g$(e),f=[];for(let d of t||[]){let p=await D(d),m=y$(d,p);i&&c<o&&(m=m.filter(g=>(g.ts??0)<=c)),f.push({track:d,events:m,brief:await b$(d)})}return{window:l,...wb(f,{windowMs:u,now:c})}}var kn=36e5,xb=24*kn,k$=400,x$=12,Yr=t=>Math.round(t*1e4)/1e4;function S$({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 Xr?String(t||"7d"):"7d",i=Xr[a];return{key:a,since:i==null?null:r-i,until:r}}var v$=(t,e)=>t!=null&&e-t<=48*kn?kn:xb;function yb(t,e){if(e===kn)return Math.floor(t/kn)*kn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function T$(t,e){if(e===kn)return t+kn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var bb=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),kb=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},A$=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Yr(n)]));async function Sb(t,e={}){let n=e.now??Date.now(),r=S$({...e,now:n}),o=r.since==null?xb:v$(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=bb(),f=0,d=0,p=0,m=0,g=0,y=new Set,b=new Map,S=[],E=[],M=[],z=null;for(let v of t||[]){let H=(await D(v)).filter(w=>w.type==="cost.recorded");if(!H.length){v.archivedAt||M.push(v.name||v.id);continue}let nt=[];for(let w of H){let h=w.ts??0;s(h)?nt.push(w):i(h)&&(g+=oa(w).usd)}if(!nt.length)continue;let ot=ue(nt,{boxLabels:rd(v)});f+=ot.totalUsd,d+=ot.turns,p+=ot.repricedTurns||0,m+=ot.unpricedTurns||0;for(let w of ot.unpricedModels||[])y.add(w);kb(u,ot.totalTokens);for(let[w,h]of Object.entries(ot.byModel))c[w]||={usd:0,events:0,tokens:bb()},c[w].usd=Yr(c[w].usd+h.usd),c[w].events+=h.events,kb(c[w].tokens,h.tokens);for(let[w,h]of Object.entries(ot.byProvider))l[w]||={usd:0,events:0},l[w].usd=Yr(l[w].usd+h.usd),l[w].events+=h.events;for(let w of nt){let{usd:h}=oa(w);if(!h)continue;let T=yb(w.ts??0,o),_=b.get(T)??{t:T,total:0,byProvider:{}};_.total+=h;let O=w.provider||"unknown";_.byProvider[O]=(_.byProvider[O]||0)+h,b.set(T,_),(z==null||T<z)&&(z=T)}S.push({id:v.id,name:v.name||v.id,archived:!!v.archivedAt,usd:ot.totalUsd,turns:ot.turns,providers:Object.keys(ot.byProvider)});for(let[w,h]of Object.entries(ot.byBox))E.push({trackId:v.id,trackName:v.name||v.id,boxId:w,...h})}let $=r.since!=null?yb(r.since,o):z,j=[];for(let v=$,C=0;v!=null&&v<r.until&&C<k$;v=T$(v,o),C++){let H=b.get(v);j.push(H?{t:v,total:Yr(H.total),byProvider:A$(H.byProvider)}:{t:v,total:0,byProvider:{}})}return S.sort((v,C)=>C.usd-v.usd),E.sort((v,C)=>C.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:j,fleet:{totalUsd:Yr(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:M},prior:a!=null?{totalUsd:Yr(g),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:E.slice(0,x$)}}import Zr from"node:fs/promises";import E$ from"node:fs";import It from"node:path";import _$ from"node:os";import{execFile as I$}from"node:child_process";import{promisify as R$}from"node:util";var $$=R$(I$),vb=process.env.BOSTRAT_STATE_DIR||It.join(process.env.HOME||_$.tmpdir(),".bostrat"),di=!1,od=null;async function sd(t,e){let n=await Zr.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await Zr.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Zr.mkdir(It.dirname(e),{recursive:!0}),await Zr.copyFile(t,e),!0)}var O$=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 Zr.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(O$.has(s.name)||n?.has(s.name))continue;let a=It.join(t,s.name);s.isDirectory()?r+=await fi(a,It.join(e,s.name)):s.isFile()&&(r+=await sd(a,It.join(e,s.name))?1:0)}return r}async function Tb({stateDir:t=vb,dest:e=process.env.BOSTRAT_SYNC_DIR||`${vb}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Zr.mkdir(e,{recursive:!0});let o=0;o+=await sd(It.join(t,"tracks.json"),It.join(e,"tracks.json"))?1:0,o+=await sd(It.join(t,"events.jsonl"),It.join(e,"events.jsonl"))?1:0,o+=await fi(It.join(t,"events"),It.join(e,"events")),o+=await fi(It.join(t,"memory"),It.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await fi(It.join(u.cwd,".bostrat"),It.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>$$("git",["-C",e,...u]);E$.existsSync(It.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 ji(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||di))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,di&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),di=!1,od=null}catch(u){di=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
390
+ `).slice(-3).join(" | ").slice(0,300);f!==od&&(od=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 C$}from"node:child_process";import{promisify as M$}from"node:util";var N$=M$(C$),P$=12e4,B$=6e4;function D$(t){return`refs/bostrat/wip/${t}`}function _b(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function j$(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:P$}var pi=(t,e,n=15e3)=>N$("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),Ab=new Map,Eb=new Set;async function L$(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await pi(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await pi(r,["remote","get-url","origin"])}catch{return Eb.has(r)||(Eb.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 Ko(r,"bostrat: wip snapshot"),{stdout:s}=await pi(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Ab.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=D$(n);return await pi(r,["push","--force","origin",`${o}:${i}`],B$),Ab.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(`
391
+ `)[0]}`),{pushed:!1,reason:"push-failed"}}}function Ib({listTracks:t,sweepMs:e=j$(),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 L$(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 F$}from"node:child_process";import{promisify as U$}from"node:util";import{randomUUID as H$}from"node:crypto";import W$ from"node:fs";import q$ from"node:os";import K$ from"node:path";var Sn=U$(F$),mi=t=>new Promise(e=>setTimeout(e,t)),vn=Qi,ad=process.env.BOSTRAT_USAGE_PROBE_CWD||K$.join(q$.homedir(),".bostrat","usage-probe"),G$=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,V$=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,z$=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Rb=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,xn=null,fs=null,ps=null;async function J$(){try{return await Sn(N,P("has-session","-t",vn)),!0}catch{return!1}}function X$(){xn&&clearTimeout(xn),xn=setTimeout(()=>Y$().catch(()=>{}),G$),xn.unref&&xn.unref()}async function Y$(){xn&&(clearTimeout(xn),xn=null);try{await Sn(N,P("kill-session","-t",vn))}catch{}}async function Z$(){let t=Date.now()+z$;for(;;){let e=await id().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await mi(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await mi(500)}}async function Q$(){if(await J$())return;if(!await Je())throw new Error("host tmux server not running \u2014 start globo-tmux.service");W$.mkdirSync(ad,{recursive:!0}),await Ke(ad);let t=H$();await Sn(N,P("new-session","-d","-s",vn,"-c",ad,"-x","200","-y","60"));let e=`${Zi} --session-id ${t}`;await Sn(N,P("send-keys","-t",vn,e,"Enter")),await Z$()}async function id(){let{stdout:t}=await Sn(N,P("capture-pane","-t",vn,"-p"));return t}async function tO(){await Q$(),await Sn(N,P("send-keys","-t",vn,"-l","/usage")),await Sn(N,P("send-keys","-t",vn,"Enter"));let t=Date.now()+V$,e="";for(;await mi(400),e=await id(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await mi(900),e=await id(),await Sn(N,P("send-keys","-t",vn,"Escape")).catch(()=>{}),X$(),e}function eO(t){let e=String(t||"").split(`
392
+ `).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 nO(t){let e=String(t||"").split(`
393
+ `).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 $b({fresh:t=!1}={}){return!t&&fs&&Date.now()-fs.at<Rb?fs.data:ps||(ps=(async()=>{try{let e=await tO(),n=eO(e),r={...n,contributors:nO(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return fs={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 fs={at:Date.now()-(Rb-3e3),data:n},n}finally{ps=null}})(),ps)}var rO=262144;function oO(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 Ob(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:oO(t.resets_in_seconds)}:null}function sO(t){let e=String(t||"").split(`
394
+ `),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:Ob(i.primary),week:Ob(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 cd(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Un(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 Nt(n.file,rO),o=sO(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 Cb(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 hi({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 cd(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await cd(null))}return n.has("cursor")&&r.push(await hi({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function hi(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?cd(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await $b({fresh:e})}}function aO(t=process.env){return!!(zt(t)&&t.BOSTRAT_PLANE_TOKEN)}var iO=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),wi=(t,e)=>Object.assign(new Error(t),{status:e});async function Mb(t,e){if(!aO(e))throw wi("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${zt(e)}${t}`,{headers:iO(e)})}catch(o){throw wi(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw wi(r?.error||`plane replied ${n.status}`,n.status);return r}async function Nb(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await Mb(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function Pb(t,{env:e=process.env}={}){if(!t)throw wi("message id required",400);return(await Mb(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function ld(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 cO(t){let[e,n]=t.split("/"),r=ld(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 Bb(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(cO).filter(Boolean)}function lO(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function Db(t){let e=Bb(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:lO,allowed(r){if(n)return!0;let o=ld((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function jb(t,e){let n=Bb(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=ld(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import Lb from"node:crypto";import gi from"node:fs";import uO from"node:path";function Fb(t=process.env,{stateDir:e=sb,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=uO.join(e,"api-token");try{let a=gi.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=Lb.randomBytes(32).toString("base64url");try{gi.mkdirSync(e,{recursive:!0}),gi.writeFileSync(o,s+`
395
+ `,{mode:384}),gi.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function ms(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 Ub(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:Lb.timingSafeEqual(n,r)}function Hb(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 dO=[/^\/api\/github\/token\b/],fO=t=>dO.some(e=>e.test(t));function Wb({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=Hb(o);return Ub(c,t)?a():fO(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||ms(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 qb(t,e,{enforce:n=!0}={}){return!n||Ub(Hb(t),e)?!0:ms(t)}import pO from"node:http";import mO from"node:net";function Kb(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=Kb(t.headers.host);return e?{slug:e,view:Cc(e)}:null}var hO=t=>`<!doctype html><meta charset="utf-8">
395
396
  <meta http-equiv="refresh" content="3">
396
397
  <title>${t.name} \u2014 starting</title>
397
398
  <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
398
399
  <div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
399
- <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function Pb(t,e,n){let r=Nb(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(eO(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=Q$.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 Bb(t,e,n){let r=Nb(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
400
+ <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function Gb(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(hO(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=pO.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 Vb(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
400
401
  \r
401
402
  bostrat: no live view "${s}"\r
402
- `),e.destroy(),!0;let a=tO.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
403
+ `),e.destroy(),!0;let a=mO.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
403
404
  `)+`\r
404
405
  \r
405
- `),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 Db(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}.${nO(n.host)}/`}function nO(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 Cb(e)&&$c(n[0])?n.slice(1).join("."):e}var jb=10,Lb=200,rO=120,oO=24,sO=/^\d{4}-\d{2}-\d{2}$/;function od(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var aO=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function Fb(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 iO(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=$o(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(`
406
- `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,rO)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${oO}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>Lb)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${Lb}-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>jb)throw new Error(`${r.length} bullets \u2014 over the ${jb}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function Ub(t,{repos:e=od()}={}){let n=new Set(e);return(await be()).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 Hb(t,{md:e,day:n=null}){let r=iO(e),o=n==null||n===""?aO():String(n);if(!sO.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await $({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}import{execFile as cO}from"node:child_process";import{promisify as lO}from"node:util";var ad=lO(cO),Wb=3e4,uO=30*6e4,dO=Wb,fO=10*6e4,pO=10080*6e4,mO=60*6e4,hO=8,wO=1440*6e4,gO=3,qb="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",sd=60*6e4,yO=15*6e4,yi=new Map,bi=new Map,bO=/could not resolve|not found|no pull requests?/i,kO=t=>bO.test(String(t?.message||t||""));function xO(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<sd)}var Kb={fast:dO,slow:fO,decay:mO};function Gb(t,e){return t.armedAt&&e-t.armedAt<uO||xO(t,e)?"fast":e-(t.firstSeen||0)>pO?"decay":"slow"}function SO(t,e){return Kb[Gb(t,e)]}function ks(t,e,n,r){let o=t.lanes?.get(ft(e.repo,e.number))||{};return Va({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function vO(t,e){Le("watch");try{let{stdout:n}=await ad("gh",["pr","view",String(e),"--repo",t,"--json",qb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await ae({slug:t})}});return JSON.parse(n||"null")}catch(n){throw un(n)&&dn(n),n}}async function TO(t){Le("watch");try{let{stdout:e}=await ad("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",qb],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await ae({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw un(e)&&dn(e),e}}async function AO(){let t=new Map;for(let e of await ot().catch(()=>[]))t.set(e.id,e);return t}async function EO(t,e){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function _O(t,e){let n=await zn(t);return n?Br(n,e):null}async function IO(t,e,n){let r=await zn(t);if(!r)throw new Error(`no local checkout for ${t}`);return Dr(r,e,{method:n})}async function RO(t,e){let n=await zn(t);if(!n)throw new Error(`no local checkout for ${t}`);return ts(n,e)}async function $O(t,e){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function eo(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 OO=10*6e4;async function MO(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>wO){await eo(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 eo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await eo(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<OO)return;await eo(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 eo(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 eo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await zb(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function gs(t,e,n){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function CO(t,e){try{return await ad("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function ys(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 NO(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 Ta(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Ea(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<yO;await Vm(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await zm(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await ys(a,"pushed",t.number,f=>f.payload?.sha===s)&&await gs(a,"pushed",{...bs(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>sd)await Aa(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 Aa(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=ks(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await ys(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await gs(a,"ci-fixed",{...bs(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Aa(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>sd)await _a(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await _a(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){ks(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await ys(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await gs(a,"reconciled",{...bs(t,e),base:i.baseRefName||null}).catch(()=>{}),await _a(t.repo,t.number).catch(()=>{})}}}}async function PO(t,e,n,r){let o=ks(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await ys(s,"closed",t.number)&&await gs(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 BO(t,e,n){ks(n,t,"lost",null);let r=e.get(t.trackId);r&&!await ys(r,"lost",t.number)&&await gs(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 zb(t,e,n,r,o={}){let s=ks(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await 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 DO(t={}){let{now:e=Date.now(),auth:n=iu,view:r=vO,list:o=TO,links:s=Xm,trackMap:a=AO,emitMerged:i=EO,mark:c=sn,archiveIfArmed:l=pn,notifyIfArmed:u=mn,notifyIfResolved:f=Nu,batchThreshold:d=gO,detail:m=_O,merge:h=IO,updateBranch:w=RO,lookup:g=Rt,disarm:b=Lo,emitDisarmed:S=$O,localHasCommit:A=CO}=t;if(!(await n().catch(()=>null))?.authenticated||Ua()>0)return 0;let O=(await s().catch(()=>[])).filter(p=>e-(yi.get(ft(p.repo,p.number))||0)>=SO(p,e));if(!O.length)return 0;let F=Date.now(),T=new Map,M=new Map;for(let p of O){let v=Gb(p,e);T.set(ft(p.repo,p.number),{lane:v,intervalMs:Kb[v]}),yi.set(ft(p.repo,p.number),e),M.has(p.repo)||M.set(p.repo,[]),M.get(p.repo).push(p)}let W=await a().catch(()=>new Map),st={now:e,lanes:T,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:m,merge:h,updateBranch:w,lookup:g,disarm:b,emitDisarmed:S,localHasCommit:A};for(let[p,v]of M){let E,rt=new Set;if(v.length>d)E=await o(p).catch(()=>null);else{E=new Map;for(let I of v)try{let R=await r(p,I.number);R?.number!=null&&E.set(Number(R.number),R)}catch(R){kO(R)&&rt.add(ft(I.repo,I.number))}}if(E)for(let I of v){let R=ft(I.repo,I.number),_=E.get(Number(I.number));if(!_){if(rt.has(R)){let P=(bi.get(R)||0)+1;bi.set(R,P),P>=hO&&(await BO(I,W,st),bi.delete(R),yi.delete(R))}continue}bi.delete(R),_.state==="MERGED"||_.state==="CLOSED"?(_.state==="MERGED"?await zb(I,_,W,st):await PO(I,_,W,st),yi.delete(R)):(await NO(I,_,W,st).catch(()=>{}),I.autoMerge&&await MO(I,W,st).catch(()=>{}))}}let y={};for(let{lane:p}of T.values())y[p]=(y[p]||0)+1;return console.log(`[pr-watch] probed ${O.length} link(s) (${Object.entries(y).map(([p,v])=>`${p} ${v}`).join(", ")}) in ${Date.now()-F}ms`),O.length}function Vb(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await DO()}catch{}finally{t=!1}}},Wb)}import Ke from"node:fs/promises";import no from"node:path";import ek from"node:os";import Jb from"node:fs/promises";import ki from"node:path";import{execFile as jO}from"node:child_process";import{promisify as LO}from"node:util";import{fileURLToPath as FO}from"node:url";var UO=LO(jO),Yb=ki.dirname(FO(import.meta.url)),HO=ki.resolve(Yb,"..","..","client","dist"),WO="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",qO=new Map([...WO].map((t,e)=>[t,e]));function KO(t){let e=[],n=0,r=0;for(let o of t){let s=qO.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 GO(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=KO(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 zO(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 Xb=new Map;async function VO(t){let e;try{e=await Jb.stat(t)}catch{return null}let n=Xb.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await Jb.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:GO(r.mappings||"")};return Xb.set(t,o),o}catch{return null}}var id=null;function JO(){return id||(id=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):UO("git",["rev-parse","--short","HEAD"],{cwd:ki.resolve(Yb,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),id}var XO=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function YO(t,{distDir:e=HO}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(XO)){let[a,i,c,l]=s,u=await VO(ki.join(e,"assets",`${i}.map`)),f=u?zO(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 Zb(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await JO();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await YO(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var ZO=()=>process.env.BOSTRAT_STATE_DIR||no.join(process.env.HOME||ek.tmpdir(),".bostrat"),vn=()=>no.join(ZO(),"telemetry"),nk=()=>no.join(vn(),".relay-offsets.json"),QO=50,tM=14,eM=3e4,Qb=500,qt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function nM(t,e){if(!t||typeof t!="object")return null;let n=qt(t.name,64);if(!n)return null;let r=Number.isFinite(t.ts)?t.ts:e,o={name:n,ts:Math.abs(r-e)<168*3600*1e3?r:e,receivedAt:e,anonId:qt(t.anonId,64),sessionId:qt(t.sessionId,64),userId:qt(t.userId,64),release:qt(t.release,64),appVersion:qt(t.appVersion,32),route:qt(t.route,128),deviceClass:t.deviceClass==="phone"||t.deviceClass==="desktop"?t.deviceClass:""};return t.props&&typeof t.props=="object"&&!Array.isArray(t.props)&&(JSON.stringify(t.props).length<=8192?o.props=t.props:o.props={clamped:!0}),t.error&&typeof t.error=="object"&&(o.error={kind:qt(t.error.kind,256),message:qt(t.error.message,8192),stack:qt(t.error.stack,32768),fingerprint:qt(t.error.fingerprint,64)}),Array.isArray(t.breadcrumbs)&&(o.breadcrumbs=t.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:qt(s?.n,64),r:qt(s?.r,128)}))),o}var rk=t=>new Date(t).toISOString().slice(0,10),tk=0;async function ok(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,QO):[]).map(s=>nM(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await Zb(s);await Ke.mkdir(vn(),{recursive:!0});let o=no.join(vn(),`${rk(e)}.ndjson`);return await Ke.appendFile(o,r.map(s=>JSON.stringify(s)+`
407
- `).join(""),"utf8"),e-tk>6*3600*1e3&&(tk=e,rM(e).catch(()=>{})),r.length}async function rM(t=Date.now()){let e=rk(t-tM*24*3600*1e3),n=[];try{n=await Ke.readdir(vn())}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 Ke.rm(no.join(vn(),s),{force:!0});let o=await ak();for(let s of r)delete o[s];await ik(o)}var xi=new Map;function sk(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 ak(){try{return JSON.parse(await Ke.readFile(nk(),"utf8"))||{}}catch{return{}}}async function ik(t){await Ke.mkdir(vn(),{recursive:!0}),await Ke.writeFile(nk(),JSON.stringify(t),"utf8")}function oM(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||ek.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 sM({fetchImpl:t=fetch}={}){let e=await wm();if(!e)return{sent:0,disabled:!0};let n;try{n=(await Ke.readdir(vn())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await ak(),o=0;for(let s of n){let a=no.join(vn(),s),i=r[s]||0,c;try{let d=await Ke.open(a,"r");try{let m=(await d.stat()).size;if(m<=i)continue;c=Buffer.alloc(m-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(`
408
- `).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=Qb){let m=f.slice(d,d+Qb).map(w=>oM(w)),h=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:m})}).catch(()=>null);if(!h||!h.ok)return{sent:o,disabled:!1};o+=m.length}r[s]=i+l+1,await ik(r)}return{sent:o,disabled:!1}}var cd=null,ld=!1;function ck(){cd||(cd=setInterval(async()=>{if(!ld){ld=!0;try{await sM()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{ld=!1}}},eM),cd.unref?.())}import vi from"node:fs/promises";import pk from"node:path";import aM from"node:os";var mk=()=>process.env.BOSTRAT_STATE_DIR||pk.join(process.env.HOME||aM.tmpdir(),".bostrat"),hk=()=>pk.join(mk(),"dictation-glossary.json"),iM=100,cM=500,lM=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Ti=null,ud=null,dd=Promise.resolve();function lk(){let t={},e=Date.now();for(let n of lM)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function wk(){return Ti||(ud??=vi.readFile(hk(),"utf8").then(t=>JSON.parse(t)?.terms??lk()).catch(()=>lk()).then(t=>Ti=t).finally(()=>ud=null),ud)}async function uk(){let t=JSON.stringify({terms:Ti},null,2),e=hk(),n=`${e}.${process.pid}.tmp`;await vi.mkdir(mk(),{recursive:!0}),await vi.writeFile(n,t),await vi.rename(n,e)}function uM(){return dd=dd.then(uk,uk),dd.catch(()=>{})}async function fd(){return Object.values(await wk()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var dM=t=>Math.max(1,Math.ceil(t.length/4));async function fM(){let t=[],e=0;for(let{term:n}of await fd()){let r=dM(n);if(!(e+r>cM)&&(t.push(n),e+=r,t.length>=iM))break}return t}async function pM(t,e){let n=await wk(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var dk=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Si=t=>t.toLowerCase();function fk(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function gk(t,e){let n=dk(t),r=dk(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]=Si(n[u])===Si(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;)Si(n[c])===Si(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(fk(r[l])&&i.add(r[l]),l++);for(;l<s;l++)fk(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await pM(u,"learned");return await uM(),[...i]}var xs=null;function mM(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 yk(){if(!xs)return null;let t=await Mo();if(!t||t.apiKey.slice(-6)!==xs.keyTail)return null;let{keyTail:e,...n}=xs;return n}async function bk(t,e){let n=await Mo();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 fM())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=mM(o.status,i);c.persistent&&(xs={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 xs=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var pd={"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 hM(t){if(!t)return null;let e=String(t).trim();if(pd[e])return e;let n=null;for(let r of Object.keys(pd))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function wM(t,e={}){let n=pd[hM(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 gM(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 Ss=null;async function yM(){if(!Ss)return null;let t=await ya();if(!t||t.apiKey.slice(-6)!==Ss.keyTail)return null;let{keyTail:e,...n}=Ss;return n}async function bM(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=gM(e.status,o);s.persistent&&(Ss={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}Ss=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 kM={openai:bM};async function kk(){let t=await ya();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await yM()}:{configured:!1}}async function xk(){let t=await ya();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 kM[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function Sk(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=wM(e,s),i=await $(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var Ik=lt.dirname(SM(import.meta.url)),vk=Number(process.env.PORT)||3362,Ai=Jy(process.env),Rk=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,ro=Eb(process.env.BOSTRAT_ALLOW_IPS),$k=Ib(),Ok=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=ve();x.disable("x-powered-by");x.use((t,e,n)=>{if(ws(t)||ro.allowed(ro.clientIp(t)))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(Pb);x.use(Ob({token:$k,enforce:Ok}));x.use("/api/mocks",ve.json({limit:"1mb"}));x.use("/api/mocks",ve.raw({type:"image/png",limit:"24mb"}));x.use("/api/runbooks",ve.json({limit:"1mb"}));x.use("/api/telemetry",ve.json({limit:"256kb"}));x.use("/api/hook",ve.json({limit:"2mb"}));x.use("/api/dictation",ve.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",ve.raw({type:()=>!0,limit:"25mb"}));x.use(ve.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Ft,hostTmuxAlive:await Je(),autoTriggerPct:Rk})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await En()})});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 _n(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 Y(n))return e.status(404).json({error:"session not found"});try{await ce(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 Y(n);if(!r)return e.status(404).json({error:"session not found"});let o=await go(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 ep(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=ta(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 Tk=10;x.get("/api/tracks",async(t,e)=>{let n=await ot(),r=n.filter(s=>s.archivedAt),o=n;if(t.query.archived!=="all"&&r.length>Tk){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,Tk).map(a=>a.id));o=n.filter(a=>!a.archivedAt||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:ni,agentAvailability:await Iu(),tickets:await ka()})});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 nb(n);if(r.cursorUsage=ru(),t.query.format==="text")return e.type("text/plain").send(rb(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 ab(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 db(await ot(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=ru(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,e.json(n)}catch(n){e.status(500).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 xb(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 eh(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 nh(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 Ay()})}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 Sd(t.get(_s),{byToken:ry,legacy:()=>ny(t.body?.session_id,t.body?.cwd)});if(n==="refused")return Is("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&&Is("mismatch",{track:r.track.id,box:r.box.name}),Is(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await zy(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(!sk(ro.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await ok(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 Te(),r=new Set((await Promise.all(n.map(a=>tt(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await co()).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 Ms(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:Os(),calls:Bw(),latency:cg()})});x.post("/api/github/refresh",(t,e)=>{Md(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await vb(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 Tb(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function yd(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 Ei({repo:t=null,number:e=null,branch:n=null}){let r=t?await tt(t):null;if(r&&e!=null){let s=await Rt(r,e),a=s&&await Q(s.trackId);if(a)return a}return n?(await ot()).find(s=>s.workspace?.branch===n&&yd(s,t,r))??null:null}async function Mk({repo:t=null,number:e=null,branch:n=null}){let r=await Ei({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await tt(t).catch(()=>null):null;return(await ot()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&yd(a,t,o))??null}async function se(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Ei({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await tt(s).catch(()=>null):null);await $({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function EM(t){let e=(await ot()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await tt(n.path);if(r)for(let o of n.prs){if(await Rt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&yd(a,n.path,r));s&&await Ht({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 hg();await EM(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(!Qo.has(w))throw new Error(`invalid merge method: ${w}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let b=await tt((await Fe(n)).path);if(!b)throw new Error("repo has no GitHub remote");let S=await Ei({repo:n,number:g,branch:l}).catch(()=>null);if(!S)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await ts(n,g),await Ht({repo:b,number:g,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await jo(b,g,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Sr(b,g,i===!0),await Ia(b,g,c===!0&&i!==!0),xe(),await se("auto-armed",{number:g,url:f,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:g,auto:!0})}let d=await Dr(n,r,{method:o||"squash"});await se("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let m=await tt(n).catch(()=>null),h=!1;if(m&&(i===!0&&await Sr(m,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Ia(m,d.number,!0).catch(()=>{}),await mn(m,d.number).catch(()=>{}),h=await pn(m,d.number).catch(()=>!1),await sn(m,d.number,"merged").catch(()=>{})),i===!0&&!h){let w=await Ei({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await qr(w.id).catch(()=>{})}e.json(d)}catch(d){let m=d?.message||String(d),h=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(m)?400:502;e.status(h).json({error:m})}});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 tt((await Fe(n)).path).catch(()=>null),l=c?await Lo(c,i).catch(()=>null):null;try{await uu(n,i)}catch(u){if(!l)throw u}xe(),await se("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 tt(n).catch(()=>null),s=await bg(n,{method:r||"merge",onMerged:a=>{se("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&mn(o,a.number).catch(()=>{}).then(()=>pn(o,a.number)).catch(()=>{}).then(()=>sn(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:yg()})});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(!Qo.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 be()).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 Sr(d.repo,d.number,o===!0).catch(()=>{}),await Ia(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let m={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(m);try{let h=await zn(d.repo);if(!h)throw new Error(`no local checkout for ${d.repo}`);let w=await Br(h,d.number);f.push({r:m,l:d,repoPath:h,pr:w,plan:wg(w,{update:r!==!1})})}catch(h){m.ok=!1,m.action="failed",m.reason=h?.message||String(h)}}for(let{r:d,l:m,repoPath:h,pr:w,plan:g}of gg(f))try{if(g.action==="skip")d.action="skipped",d.reason=g.reason;else if(g.action==="merge")await Dr(h,m.number,{method:i}),await se("merged",{number:m.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c}),await mn(m.repo,m.number).catch(()=>{}),await pn(m.repo,m.number).catch(()=>{}),await sn(m.repo,m.number,"merged").catch(()=>{}),d.action="merged";else{if(g.action==="update-arm"&&(await ts(h,m.number).catch(()=>{}),d.action="updated-armed"),!await jo(m.repo,m.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await se("auto-armed",{number:m.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(b){d.ok=!1,d.action="failed",d.reason=b?.message||String(b)}if(xe(),!u.some(d=>d.ok))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 be()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Lo(a.repo,a.number).catch(()=>null),c=await zn(a.repo).catch(()=>null);c&&await uu(c,a.number).catch(()=>{}),i&&(await se("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}))}xe(),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=Bt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let g=u.find(b=>b.name===w||b.repo===w||b.base===w||b.dir===w);if(!g)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return g});else if(f=await Jo(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let m=[];for(let w of f)try{let g=await fu(w.dir,{title:r,body:o,base:s}),b=w.repo||await tt(w.dir).catch(()=>null);b&&g.number&&await Ht({repo:b,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await se("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??w.name}}),m.push({repo:w.name,slug:b??null,ok:!0,url:g.url,number:g.number})}catch(g){m.push({repo:w.name,slug:w.repo??null,ok:!1,error:g?.message||String(g)})}let h=m.filter(w=>w.ok);return h.length?e.json({ok:!0,multi:!0,results:m,url:h[0].url,number:h[0].number}):e.status(502).json({error:m.map(w=>`${w.repo}: ${w.error}`).join("; "),results:m})}let c=await fu(i.cwd,{title:r,body:o,base:s}),l=await tt(i.cwd).catch(()=>null);l&&c.number&&await Ht({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await se("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 Jo(n),o=new Map(r.map(a=>[a.base,a])),s=Bt(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 wy(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 gy(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 Br(n,r),s=await tt(n).catch(()=>null),a=await Mk({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=Bt(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await kg(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?lt.join("ci",i.name):"ci",f=lt.join(a.cwd,".bostrat",u),d=lt.join(".bostrat",u,`pr-${o.number}-fail.log`);await jt.promises.mkdir(f,{recursive:!0}),await jt.promises.writeFile(lt.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
406
+ `),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 zb(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}.${wO(n.host)}/`}function wO(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 Kb(e)&&Cc(n[0])?n.slice(1).join("."):e}var Jb=10,Xb=200,gO=120,yO=24,bO=/^\d{4}-\d{2}-\d{2}$/;function ud(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var kO=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function Yb(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 xO(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Io(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(`
407
+ `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,gO)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${yO}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>Xb)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${Xb}-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>Jb)throw new Error(`${r.length} bullets \u2014 over the ${Jb}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function Zb(t,{repos:e=ud()}={}){let n=new Set(e);return(await ye()).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 Qb(t,{md:e,day:n=null}){let r=xO(e),o=n==null||n===""?kO():String(n);if(!bO.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 SO}from"node:child_process";import{promisify as vO}from"node:util";var fd=vO(SO),tk=3e4,TO=30*6e4,AO=tk,EO=10*6e4,_O=10080*6e4,IO=60*6e4,RO=8,$O=1440*6e4,OO=3,ek="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",dd=60*6e4,CO=15*6e4,bi=new Map,ki=new Map,MO=/could not resolve|not found|no pull requests?/i,NO=t=>MO.test(String(t?.message||t||""));function PO(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<dd)}var nk={fast:AO,slow:EO,decay:IO};function rk(t,e){return t.armedAt&&e-t.armedAt<TO||PO(t,e)?"fast":e-(t.firstSeen||0)>_O?"decay":"slow"}function BO(t,e){return nk[rk(t,e)]}function ys(t,e,n,r){let o=t.lanes?.get(U(e.repo,e.number))||{};return Ga({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function DO(t,e){je("watch");try{let{stdout:n}=await fd("gh",["pr","view",String(e),"--repo",t,"--json",ek],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await ae({slug:t})}});return JSON.parse(n||"null")}catch(n){throw dn(n)&&fn(n),n}}async function jO(t){je("watch");try{let{stdout:e}=await fd("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",ek],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await ae({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw dn(e)&&fn(e),e}}async function LO(){let t=new Map;for(let e of await rt().catch(()=>[]))t.set(e.id,e);return t}async function FO(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 UO(t,e){let n=await zn(t);return n?Br(n,e):null}async function HO(t,e,n){let r=await zn(t);if(!r)throw new Error(`no local checkout for ${t}`);return Dr(r,e,{method:n})}async function WO(t,e){let n=await zn(t);if(!n)throw new Error(`no local checkout for ${t}`);return Zo(n,e)}async function qO(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 Qr(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 KO=10*6e4;async function GO(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>$O){await Qr(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 Qr(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Qr(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<KO)return;await Qr(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 Qr(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 Qr(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await ok(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function hs(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 VO(t,e){try{return await fd("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function ws(t,e,n,r=()=>!0){return(await D(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var gs=(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 zO(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 Sa(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Ta(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<CO;await oh(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await rh(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await ws(a,"pushed",t.number,f=>f.payload?.sha===s)&&await hs(a,"pushed",{...gs(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>dd)await va(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 va(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=ys(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await ws(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await hs(a,"ci-fixed",{...gs(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await va(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>dd)await Aa(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Aa(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){ys(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await ws(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await hs(a,"reconciled",{...gs(t,e),base:i.baseRefName||null}).catch(()=>{}),await Aa(t.repo,t.number).catch(()=>{})}}}}async function JO(t,e,n,r){let o=ys(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await ws(s,"closed",t.number)&&await hs(s,"closed",{...gs(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 XO(t,e,n){ys(n,t,"lost",null);let r=e.get(t.trackId);r&&!await ws(r,"lost",t.number)&&await hs(r,"lost",gs(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function ok(t,e,n,r,o={}){let s=ys(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await D(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 YO(t={}){let{now:e=Date.now(),auth:n=du,view:r=DO,list:o=jO,links:s=ah,trackMap:a=LO,emitMerged:i=FO,mark:c=an,archiveIfArmed:l=mn,notifyIfArmed:u=hn,notifyIfResolved:f=ju,batchThreshold:d=OO,detail:p=UO,merge:m=HO,updateBranch:g=WO,lookup:y=$t,disarm:b=Do,emitDisarmed:S=qO,localHasCommit:E=VO}=t;if(!(await n().catch(()=>null))?.authenticated||La()>0)return 0;let $=(await s().catch(()=>[])).filter(w=>e-(bi.get(U(w.repo,w.number))||0)>=BO(w,e));if(!$.length)return 0;let j=Date.now(),v=new Map,C=new Map;for(let w of $){let h=rk(w,e);v.set(U(w.repo,w.number),{lane:h,intervalMs:nk[h]}),bi.set(U(w.repo,w.number),e),C.has(w.repo)||C.set(w.repo,[]),C.get(w.repo).push(w)}let H=await a().catch(()=>new Map),nt={now:e,lanes:v,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:g,lookup:y,disarm:b,emitDisarmed:S,localHasCommit:E};for(let[w,h]of C){let T,_=new Set;if(h.length>d)T=await o(w).catch(()=>null);else{T=new Map;for(let O of h)try{let I=await r(w,O.number);I?.number!=null&&T.set(Number(I.number),I)}catch(I){NO(I)&&_.add(U(O.repo,O.number))}}if(T)for(let O of h){let I=U(O.repo,O.number),A=T.get(Number(O.number));if(!A){if(_.has(I)){let K=(ki.get(I)||0)+1;ki.set(I,K),K>=RO&&(await XO(O,H,nt),ki.delete(I),bi.delete(I))}continue}ki.delete(I),A.state==="MERGED"||A.state==="CLOSED"?(A.state==="MERGED"?await ok(O,A,H,nt):await JO(O,A,H,nt),bi.delete(I)):(await zO(O,A,H,nt).catch(()=>{}),O.autoMerge&&await GO(O,H,nt).catch(()=>{}))}}let ot={};for(let{lane:w}of v.values())ot[w]=(ot[w]||0)+1;return console.log(`[pr-watch] probed ${$.length} link(s) (${Object.entries(ot).map(([w,h])=>`${w} ${h}`).join(", ")}) in ${Date.now()-j}ms`),$.length}function sk(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await YO()}catch{}finally{t=!1}}},tk)}import qe from"node:fs/promises";import to from"node:path";import fk from"node:os";import ak from"node:fs/promises";import xi from"node:path";import{execFile as ZO}from"node:child_process";import{promisify as QO}from"node:util";import{fileURLToPath as tC}from"node:url";var eC=QO(ZO),ck=xi.dirname(tC(import.meta.url)),nC=xi.resolve(ck,"..","..","client","dist"),rC="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",oC=new Map([...rC].map((t,e)=>[t,e]));function sC(t){let e=[],n=0,r=0;for(let o of t){let s=oC.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 aC(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=sC(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 iC(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 ik=new Map;async function cC(t){let e;try{e=await ak.stat(t)}catch{return null}let n=ik.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await ak.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:aC(r.mappings||"")};return ik.set(t,o),o}catch{return null}}var pd=null;function lC(){return pd||(pd=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):eC("git",["rev-parse","--short","HEAD"],{cwd:xi.resolve(ck,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),pd}var uC=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function dC(t,{distDir:e=nC}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(uC)){let[a,i,c,l]=s,u=await cC(xi.join(e,"assets",`${i}.map`)),f=u?iC(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 lk(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await lC();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await dC(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var fC=()=>process.env.BOSTRAT_STATE_DIR||to.join(process.env.HOME||fk.tmpdir(),".bostrat"),Tn=()=>to.join(fC(),"telemetry"),pk=()=>to.join(Tn(),".relay-offsets.json"),pC=50,mC=14,hC=3e4,uk=500,Gt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function wC(t,e){if(!t||typeof t!="object")return null;let n=Gt(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:Gt(t.anonId,64),sessionId:Gt(t.sessionId,64),userId:Gt(t.userId,64),release:Gt(t.release,64),appVersion:Gt(t.appVersion,32),route:Gt(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:Gt(t.error.kind,256),message:Gt(t.error.message,8192),stack:Gt(t.error.stack,32768),fingerprint:Gt(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:Gt(s?.n,64),r:Gt(s?.r,128)}))),o}var mk=t=>new Date(t).toISOString().slice(0,10),dk=0;async function hk(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,pC):[]).map(s=>wC(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await lk(s);await qe.mkdir(Tn(),{recursive:!0});let o=to.join(Tn(),`${mk(e)}.ndjson`);return await qe.appendFile(o,r.map(s=>JSON.stringify(s)+`
408
+ `).join(""),"utf8"),e-dk>6*3600*1e3&&(dk=e,gC(e).catch(()=>{})),r.length}async function gC(t=Date.now()){let e=mk(t-mC*24*3600*1e3),n=[];try{n=await qe.readdir(Tn())}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(to.join(Tn(),s),{force:!0});let o=await gk();for(let s of r)delete o[s];await yk(o)}var Si=new Map;function wk(t,e=Date.now()){let n=t||"unknown",r=Si.get(n);return r||(Si.size>512&&Si.clear(),r={tokens:40,at:e},Si.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 gk(){try{return JSON.parse(await qe.readFile(pk(),"utf8"))||{}}catch{return{}}}async function yk(t){await qe.mkdir(Tn(),{recursive:!0}),await qe.writeFile(pk(),JSON.stringify(t),"utf8")}function yC(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||fk.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 bC({fetchImpl:t=fetch}={}){let e=await Am();if(!e)return{sent:0,disabled:!0};let n;try{n=(await qe.readdir(Tn())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await gk(),o=0;for(let s of n){let a=to.join(Tn(),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(`
409
+ `).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=uk){let p=f.slice(d,d+uk).map(g=>yC(g)),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 yk(r)}return{sent:o,disabled:!1}}var md=null,hd=!1;function bk(){md||(md=setInterval(async()=>{if(!hd){hd=!0;try{await bC()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{hd=!1}}},hC),md.unref?.())}import Ti from"node:fs/promises";import Tk from"node:path";import kC from"node:os";var Ak=()=>process.env.BOSTRAT_STATE_DIR||Tk.join(process.env.HOME||kC.tmpdir(),".bostrat"),Ek=()=>Tk.join(Ak(),"dictation-glossary.json"),xC=100,SC=500,vC=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Ai=null,wd=null,gd=Promise.resolve();function kk(){let t={},e=Date.now();for(let n of vC)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function _k(){return Ai||(wd??=Ti.readFile(Ek(),"utf8").then(t=>JSON.parse(t)?.terms??kk()).catch(()=>kk()).then(t=>Ai=t).finally(()=>wd=null),wd)}async function xk(){let t=JSON.stringify({terms:Ai},null,2),e=Ek(),n=`${e}.${process.pid}.tmp`;await Ti.mkdir(Ak(),{recursive:!0}),await Ti.writeFile(n,t),await Ti.rename(n,e)}function TC(){return gd=gd.then(xk,xk),gd.catch(()=>{})}async function yd(){return Object.values(await _k()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var AC=t=>Math.max(1,Math.ceil(t.length/4));async function EC(){let t=[],e=0;for(let{term:n}of await yd()){let r=AC(n);if(!(e+r>SC)&&(t.push(n),e+=r,t.length>=xC))break}return t}async function _C(t,e){let n=await _k(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var Sk=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),vi=t=>t.toLowerCase();function vk(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Ik(t,e){let n=Sk(t),r=Sk(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]=vi(n[u])===vi(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;)vi(n[c])===vi(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(vk(r[l])&&i.add(r[l]),l++);for(;l<s;l++)vk(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await _C(u,"learned");return await TC(),[...i]}var bs=null;function IC(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 Rk(){if(!bs)return null;let t=await $o();if(!t||t.apiKey.slice(-6)!==bs.keyTail)return null;let{keyTail:e,...n}=bs;return n}async function $k(t,e){let n=await $o();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 EC())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=IC(o.status,i);c.persistent&&(bs={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 bs=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var bd={"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 RC(t){if(!t)return null;let e=String(t).trim();if(bd[e])return e;let n=null;for(let r of Object.keys(bd))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function $C(t,e={}){let n=bd[RC(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 OC(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 ks=null;async function CC(){if(!ks)return null;let t=await ga();if(!t||t.apiKey.slice(-6)!==ks.keyTail)return null;let{keyTail:e,...n}=ks;return n}async function MC(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=OC(e.status,o);s.persistent&&(ks={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}ks=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 NC={openai:MC};async function Ok(){let t=await ga();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await CC()}:{configured:!1}}async function Ck(){let t=await ga();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 NC[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function Mk(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=$C(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 Lk=ct.dirname(BC(import.meta.url)),Nk=Number(process.env.PORT)||3362,Ei=ab(process.env),Fk=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,eo=Db(process.env.BOSTRAT_ALLOW_IPS),Td=jb(process.env.BOSTRAT_VIEW_FRONT_CIDRS,yi),Uk=Fb(),Hk=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=Se();x.disable("x-powered-by");x.use((t,e,n)=>{if(ms(t)||eo.allowed(eo.clientIp(t))||Td.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(Gb);x.use(Wb({token:Uk,enforce:Hk}));x.use("/api/mocks",Se.json({limit:"1mb"}));x.use("/api/mocks",Se.raw({type:"image/png",limit:"24mb"}));x.use("/api/runbooks",Se.json({limit:"1mb"}));x.use("/api/telemetry",Se.json({limit:"256kb"}));x.use("/api/hook",Se.json({limit:"2mb"}));x.use("/api/dictation",Se.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",Se.raw({type:()=>!0,limit:"25mb"}));x.use(Se.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Ut,hostTmuxAlive:await Je(),autoTriggerPct:Fk})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await _n()})});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 In(n,r||void 0);e.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;e.status(a).json({error:s})}});x.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});if(!await Z(n))return e.status(404).json({error:"session not found"});try{await ce(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});x.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});let r=await Z(n);if(!r)return e.status(404).json({error:"session not found"});let o=await yo(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});x.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});try{let r=await bp(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 Pk=10;x.get("/api/tracks",async(t,e)=>{let n=await rt(),r=n.filter(s=>s.archivedAt),o=n;if(t.query.archived!=="all"&&r.length>Pk){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,Pk).map(a=>a.id));o=n.filter(a=>!a.archivedAt||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:ti,agentAvailability:await Cu(),tickets:await ba()})});x.get("/api/cost",async(t,e)=>{try{let n=await rt();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await pb(n);if(r.cursorUsage=iu(),t.query.format==="text")return e.type("text/plain").send(mb(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 gb(await rt(),{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 Sb(await rt(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=iu(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,e.json(n)}catch(n){e.status(500).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 Cb(await rt(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs",async(t,e)=>{try{e.json(await fh(await rt()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs/:key",async(t,e)=>{try{let n=await ph(await rt(),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 By()})}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 Rd(t.get(As),{byToken:my,legacy:()=>py(t.body?.session_id,t.body?.cwd)});if(n==="refused")return Es("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&&Es("mismatch",{track:r.track.id,box:r.box.name}),Es(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await ob(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(!wk(eo.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await hk(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 ve(),r=new Set((await Promise.all(n.map(a=>tt(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 $s(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:Rs(),calls:Vw(),latency:bg()})});x.post("/api/github/refresh",(t,e)=>{Ld(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await Nb(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 Pb(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function Ad(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 _i({repo:t=null,number:e=null,branch:n=null}){let r=t?await tt(t):null;if(r&&e!=null){let s=await $t(r,e),a=s&&await Q(s.trackId);if(a)return a}return n?(await rt()).find(s=>s.workspace?.branch===n&&Ad(s,t,r))??null:null}async function Wk({repo:t=null,number:e=null,branch:n=null}){let r=await _i({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await tt(t).catch(()=>null):null;return(await rt()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&Ad(a,t,o))??null}async function se(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await _i({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await tt(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 FC(t){let e=(await rt()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await tt(n.path);if(r)for(let o of n.prs){if(await $t(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&Ad(a,n.path,r));s&&await Wt({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 Eg();await FC(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 g=o||"merge";if(!Yo.has(g))throw new Error(`invalid merge method: ${g}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let b=await tt((await Le(n)).path);if(!b)throw new Error("repo has no GitHub remote");let S=await _i({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 Zo(n,y),await Wt({repo:b,number:y,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Bo(b,y,{merge:{method:g}}))throw new Error("couldn't record the auto-merge arm");return await Sr(b,y,i===!0),await Ea(b,y,c===!0&&i!==!0),ke(),await se("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 Dr(n,r,{method:o||"squash"});await se("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await tt(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Sr(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Ea(p,d.number,!0).catch(()=>{}),await hn(p,d.number).catch(()=>{}),m=await mn(p,d.number).catch(()=>!1),await an(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let g=await _i({repo:n,number:d.number,branch:l}).catch(()=>null);g&&!g.archivedAt&&await qr(g.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 tt((await Le(n)).path).catch(()=>null),l=c?await Do(c,i).catch(()=>null):null;try{await mu(n,i)}catch(u){if(!l)throw u}ke(),await se("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 tt(n).catch(()=>null),s=await $g(n,{method:r||"merge",onMerged:a=>{se("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&hn(o,a.number).catch(()=>{}).then(()=>mn(o,a.number)).catch(()=>{}).then(()=>an(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:Rg()})});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(!Yo.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 ye()).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 Sr(d.repo,d.number,o===!0).catch(()=>{}),await Ea(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 zn(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let g=await Br(m,d.number);f.push({r:p,l:d,repoPath:m,pr:g,plan:_g(g,{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:g,plan:y}of Ig(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Dr(m,p.number,{method:i}),await se("merged",{number:p.number,url:g.url,title:g.title,branch:g.headRefName,repo:m,track:c}),await hn(p.repo,p.number).catch(()=>{}),await mn(p.repo,p.number).catch(()=>{}),await an(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await Zo(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Bo(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await se("auto-armed",{number:p.number,url:g.url,title:g.title,branch:g.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(ke(),!u.some(d=>d.ok))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 ye()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Do(a.repo,a.number).catch(()=>null),c=await zn(a.repo).catch(()=>null);c&&await mu(c,a.number).catch(()=>{}),i&&(await se("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}))}ke(),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=Dt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(g=>{let y=u.find(b=>b.name===g||b.repo===g||b.base===g||b.dir===g);if(!y)throw new Error(`invalid repo "${g}" \u2014 not tagged on this Track`);return y});else if(f=await Vo(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let g of f)try{let y=await wu(g.dir,{title:r,body:o,base:s}),b=g.repo||await tt(g.dir).catch(()=>null);b&&y.number&&await Wt({repo:b,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await se("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??g.name}}),p.push({repo:g.name,slug:b??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:g.name,slug:g.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(g=>g.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(g=>`${g.repo}: ${g.error}`).join("; "),results:p})}let c=await wu(i.cwd,{title:r,body:o,base:s}),l=await tt(i.cwd).catch(()=>null);l&&c.number&&await Wt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await se("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 Vo(n),o=new Map(r.map(a=>[a.base,a])),s=Dt(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 _y(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 Iy(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 Br(n,r),s=await tt(n).catch(()=>null),a=await Wk({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=Dt(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await Og(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ct.join("ci",i.name):"ci",f=ct.join(a.cwd,".bostrat",u),d=ct.join(".bostrat",u,`pr-${o.number}-fail.log`);await Lt.promises.mkdir(f,{recursive:!0}),await Lt.promises.writeFile(ct.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
409
410
  ${b.log??"(no fetchable log \u2014 see link)"}
410
411
  `).join(`
411
- `));let m=l.map(b=>b.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await oe(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${h}. Failing check${l.length===1?"":"s"}: ${m}. The failure log is at ${d} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Ht({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await qm(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await se("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 Br(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await tt(n).catch(()=>null),a=await Mk({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=Bt(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 oe(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 Ht({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Km(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await se("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=vw.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 oe(a.id,ja(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await $({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&cy(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 vs=t=>e=>({...e,url:Db(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await da({trackId:t.query.track||void 0});e.json({views:n.map(vs(t)),maxLive:nn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await Vp(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:vs(t)(n),session:gr(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+lt.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Oc({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:vs(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:vs(t)(await Jp(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:vs(t)(await Xp(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 Yp(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var _M=[["linear",Nn,jc],["jira",ga,Bc]],md=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await Pc(),r={};for(let[o,s,a]of _M){let i=await s();if(!i){md.delete(o),r[o]={configured:!1};continue}let c=md.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},md.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 Pc()})}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 hm(o),i=n?await Q(n):null;!i&&r&&(i=(await ot()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+lt.sep)))||null),i&&await $({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await Q(n):null;if(!s&&r&&(s=(await ot()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+lt.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 xr(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+lt.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 $m(),ticket:await ye(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 ye(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 xr(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 Xc(t.params.id)})});var Ii=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 Nm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Ii(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await Yc(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Ii(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Pm(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Ii(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 Bm(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Ii(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+lt.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 jm(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 Dm()})}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 Lm(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 On({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 en(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 en(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ie(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(Ut(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 en(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ie(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(Ut(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(m=>m.cwd&&(a===m.cwd||a.startsWith(m.cwd+lt.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 Ip({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 Rp(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 Op(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await oe(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 Pg(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Mp(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 $g({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 Og(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+lt.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 Mg({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 Cg(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 Ng(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+lt.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await ma({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 Cc(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 Cc(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=nm(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(Ce(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+lt.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 sm({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 am(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 im(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 cm(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=Fb(t.query.since||null);e.json({since:n,repos:od(),prs:await Ub(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+lt.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await Hb(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 IM=120,hd=400,_i=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>hd)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${hd} cap; a notification is a nudge, put the detail in chat`});let c=$o(`${n||""}
412
- ${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(h=>h.cwd&&(s===h.cwd||s.startsWith(h.cwd+lt.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(m=>m.name===l.leadBox),f=(m,h)=>String(m).replace(/\s+/g," ").trim().slice(0,h),d={title:n?f(n,IM):l.name,body:f(i,hd),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await $(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),e.json({ok:!0,notification:d,delivered:_i?_i.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 bk(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 Mo(),terms:await fd(),problem:await yk()})});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 gk(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 kk())});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 xk())}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 Sk(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 Cu({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await xr(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:Ck(us(t.params.id)),subtasks:Kr(t.params.id).map(Ts)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await vy(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await Xc(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 qr(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 _y(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 Ru(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}
412
+ `));let p=l.map(b=>b.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",g=await oe(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 Wt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await th(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await se("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:g.leadBox,queued:!!g.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 Br(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await tt(n).catch(()=>null),a=await Wk({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=Dt(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 oe(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 Wt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await eh(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await se("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=Nw.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 D(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 oe(a.id,Ba(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&&by(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 xs=t=>e=>({...e,url:zb(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await ua({trackId:t.query.track||void 0});e.json({views:n.map(xs(t)),maxLive:nn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await rm(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:xs(t)(n),session:gr(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 rt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Mc({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:xs(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:xs(t)(await om(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:xs(t)(await sm(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 am(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var UC=[["linear",Pn,Fc],["jira",wa,jc]],kd=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await Dc(),r={};for(let[o,s,a]of UC){let i=await s();if(!i){kd.delete(o),r[o]={configured:!1};continue}let c=kd.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},kd.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 Dc()})}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 Tm(o),i=n?await Q(n):null;!i&&r&&(i=(await rt()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ct.sep)))||null),i&&await R({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});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 rt()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await xr(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 rt()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ct.sep)))||null),!o)return e.status(400).json({error:"track (or a cwd inside a Track workspace) required"});e.json({trackId:o.id,configured:await Lm(),ticket:await ge(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 ge(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 xr(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 Zc(t.params.id)})});var Ri=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 Wm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Ri(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await Qc(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Ri(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await qm(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Ri(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 Km(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Ri(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 rt()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,trackId:s.id,proposal:await Vm(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 Gm()})}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 zm(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 Cn({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 en(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 en(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=_e(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(Ht(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 en(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=_e(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(Ht(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 rt()).find(p=>p.cwd&&(a===p.cwd||a.startsWith(p.cwd+ct.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(d=>d.name===l.leadBox),f=await Dp({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 jp(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 Fp(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await oe(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 Gg(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Up(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 Ug({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 Hg(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 rt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await Wg({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 qg(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 Kg(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 rt(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ct.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await 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 Pc(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 Pc(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=dm(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(Ce(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 rt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await mm({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 hm(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 wm(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 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.get("/api/changelog/draft",async(t,e)=>{try{let n=Yb(t.query.since||null);e.json({since:n,repos:ud(),prs:await Zb(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 rt()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+ct.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await Qb(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 HC=120,xd=400,Ii=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>xd)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${xd} cap; a notification is a nudge, put the detail in chat`});let c=Io(`${n||""}
413
+ ${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 rt()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+ct.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),f=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),d={title:n?f(n,HC):l.name,body:f(i,xd),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:Ii?Ii.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 $k(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 $o(),terms:await yd(),problem:await Rk()})});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 Ik(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 Ok())});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 Ck())}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 Mk(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 Du({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await xr(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:qk(cs(t.params.id)),subtasks:Kr(t.params.id).map(Ss)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Ny(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await Zc(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 qr(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 jy(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 Mu(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}
413
414
  data: ${JSON.stringify(i)}
414
415
 
415
- `),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=Df(n.id,r),a=setInterval(()=>e.write(`: hb
416
+ `),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await D(n))i.seq>o&&r(i);let s=ep(n.id,r),a=setInterval(()=>e.write(`: hb
416
417
 
417
- `),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=ko(await L(n)),a=Vs(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 Gd()})});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 zd(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 RM=["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(`
418
- `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await oe(t.params.id,RM)})}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 Ry(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 oe(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});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 Ug(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=lt.basename(String(t.params.file||""));if(!r||!yu(lt.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(lt.join(gu(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 $M=/^[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(!$M.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=Ee(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=xM.createInterface({input:jt.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let m;try{m=JSON.parse(d)}catch{continue}if(m.uuid===o){l=m,u.close();break}}let f=l?rc(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 Mu(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",(t,e)=>{e.json({ok:!0,removed:si(t.params.id).filter(n=>n.kind!=="ship-notify")})});x.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await $u(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 xy({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 sy(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}=t.body||{};try{e.json(await dy(t.params.id,typeof n=="string"?n:""))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required/i.test(o)?400:500;e.status(s).json({error:o})}});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 hi(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await fy(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 Ny(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Ts(a)})}catch(a){let i=a?.message||String(a),c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;e.status(c).json({error:i})}});x.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:Kr(t.params.id).map(Ts)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await oy(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:Ts(Lu(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:Ts(By(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Ts(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 Bu(t.params.id,n,r!==!1,o);e.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});x.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await ly(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(Ck(us(t.params.id))??{running:!1})});function Ck(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:Bs(),paths:Zd,trace:await Yd({limit:n})})});var wd=lt.resolve(Ik,"..","..","client","dist");jt.existsSync(wd)&&(x.use(ve.static(wd)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(lt.join(wd,"index.html"))));var Nk=vM.createServer(x),OM=new AM({noServer:!0}),Pk=(t,e,n)=>{if(!(ws(t)||ro.allowed(ro.clientIp(t)))){e.write(`HTTP/1.1 403 Forbidden\r
418
+ `),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=xo(await D(n)),a=Js(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 Qd()})});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 tf(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 WC=["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(`
419
+ `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await oe(t.params.id,WC)})}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 Fy(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 oe(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});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 Zg(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=ct.basename(String(t.params.file||""));if(!r||!Su(ct.extname(r).slice(1)))return e.status(400).json({error:"not an upload path"});e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.sendFile(ct.join(xu(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 qC=/^[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(!qC.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=Ae(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=PC.createInterface({input:Lt.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?cc(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 Bu(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",(t,e)=>{e.json({ok:!0,removed:ri(t.params.id).filter(n=>n.kind!=="ship-notify")})});x.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await Nu(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 Cy({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 wy(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}=t.body||{};try{e.json(await Sy(t.params.id,typeof n=="string"?n:""))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required/i.test(o)?400:500;e.status(s).json({error:o})}});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 hi(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await vy(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 Ky(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Ss(a)})}catch(a){let i=a?.message||String(a),c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;e.status(c).json({error:i})}});x.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:Kr(t.params.id).map(Ss)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await hy(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:Ss(Wu(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:Ss(Vy(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Ss(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 Fu(t.params.id,n,r!==!1,o);e.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});x.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await ky(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(qk(cs(t.params.id))??{running:!1})});function qk(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:Ds(),paths:gf,trace:await wf({limit:n})})});var Sd=ct.resolve(Lk,"..","..","client","dist");Lt.existsSync(Sd)&&(x.use(Se.static(Sd)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ct.join(Sd,"index.html"))));var Kk=DC.createServer(x),KC=new LC({noServer:!0}),Gk=(t,e,n)=>{if(!(ms(t)||eo.allowed(eo.clientIp(t))||Td.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
419
420
  \r
420
- `),e.destroy();return}if(Bb(t,e,n))return;if(!Mb(t,$k,{enforce:Ok})){e.write(`HTTP/1.1 401 Unauthorized\r
421
+ `),e.destroy();return}if(Vb(t,e,n))return;if(!qb(t,Uk,{enforce:Hk})){e.write(`HTTP/1.1 401 Unauthorized\r
421
422
  \r
422
- `),e.destroy();return}let r=cf(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
423
+ `),e.destroy();return}let r=Ef(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
423
424
  \r
424
- `),e.destroy();return}let o=r.sessionName;if(!yt(o)){e.write(`HTTP/1.1 400 Bad Request\r
425
+ `),e.destroy();return}let o=r.sessionName;if(!wt(o)){e.write(`HTTP/1.1 400 Bad Request\r
425
426
  \r
426
- `),e.destroy();return}let{cols:s,rows:a}=r;OM.handleUpgrade(t,e,n,i=>of(i,o,{cols:s,rows:a}))};Nk.on("upgrade",Pk);var Bk=process.env.BOSTRAT_CERT_DIR||lt.join(process.env.HOME||gd.tmpdir(),".bostrat","certs"),Ak=Number(process.env.BOSTRAT_TLS_PORT)||3443,Ek=lt.join(Bk,"bostrat.globopi.key"),_k=lt.join(Bk,"bostrat.globopi.crt");if(jt.existsSync(Ek)&&jt.existsSync(_k)){let t=TM.createServer({key:jt.readFileSync(Ek),cert:jt.readFileSync(_k)},x);t.on("upgrade",Pk),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(Ak,Ai,()=>{console.log(`[bostrat-agent] https listening on ${Ai||"0.0.0.0"}:${Ak}`)})}Nk.listen(vk,Ai,()=>{console.log(`[bostrat-agent] listening on ${Ai||"0.0.0.0"}:${vk}`),console.log(`[bostrat-agent] tmux socket: ${Ft}`),console.log(`[bostrat-agent] auto-trigger default: ${Rk}%`),console.log(`[bostrat-agent] ip allowlist: ${ro.open?"OPEN (BOSTRAT_ALLOW_IPS unset)":process.env.BOSTRAT_ALLOW_IPS}`),My().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Cy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Iy().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)),ky().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)),PM(),NM(),wb()&&gb({listTracks:ot}),Vb(),Fm({resolveTrack:Q}),Gy({listTracks:ot,createTrack:Cu,archiveTrack:qr,listRepos:Te,resolveTrack:Q,sayToLead:oe,answerPrompt:$u,startLeadHandoff:Bu,interruptLead:Mu,listSubtaskJobs:Kr,cancelSubtask:Lu,readEvents:L,appendEvent:$,knownAgents:ni}),MM(),ho().then(t=>{t||console.warn(`[boot] no host tmux server on ${Ft} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),ck(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(_i=Qy({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||gd.hostname(),name:process.env.BOSTRAT_NODE_NAME||gd.hostname(),listTracks:ot,detectAgents:Iu}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),pr().catch(()=>{}),dm().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Um().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),Pp().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),jg().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{_i?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function MM(){CM(),ua().catch(()=>{}),setInterval(()=>ua().catch(()=>{}),1e4)}function CM(){for(let t of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-runbook","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let e=lt.resolve(Ik,"..","bin",t),n=lt.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!jt.existsSync(e))continue;jt.mkdirSync(n,{recursive:!0});let r=lt.join(n,t);(jt.existsSync(r)?jt.readlinkSync(r):null)!==e&&(jt.rmSync(r,{force:!0}),jt.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 NM(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await ot(),r=await pb({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 PM(){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 Ru(n.id).catch(()=>{}),await xg(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
427
+ `),e.destroy();return}let{cols:s,rows:a}=r;KC.handleUpgrade(t,e,n,i=>vf(i,o,{cols:s,rows:a}))};Kk.on("upgrade",Gk);var Vk=process.env.BOSTRAT_CERT_DIR||ct.join(process.env.HOME||vd.tmpdir(),".bostrat","certs"),Bk=Number(process.env.BOSTRAT_TLS_PORT)||3443,Dk=ct.join(Vk,"bostrat.globopi.key"),jk=ct.join(Vk,"bostrat.globopi.crt");if(Lt.existsSync(Dk)&&Lt.existsSync(jk)){let t=jC.createServer({key:Lt.readFileSync(Dk),cert:Lt.readFileSync(jk)},x);t.on("upgrade",Gk),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(Bk,Ei,()=>{console.log(`[bostrat-agent] https listening on ${Ei||"0.0.0.0"}:${Bk}`)})}Kk.listen(Nk,Ei,()=>{console.log(`[bostrat-agent] listening on ${Ei||"0.0.0.0"}:${Nk}`),console.log(`[bostrat-agent] tmux socket: ${Ut}`),console.log(`[bostrat-agent] auto-trigger default: ${Fk}%`),console.log(`[bostrat-agent] ip allowlist: ${eo.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Td.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Wy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),qy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Ly().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)),Oy().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)),JC(),zC(),_b()&&Ib({listTracks:rt}),sk(),Jm({resolveTrack:Q}),rb({listTracks:rt,createTrack:Du,archiveTrack:qr,listRepos:ve,resolveTrack:Q,sayToLead:oe,answerPrompt:Nu,startLeadHandoff:Fu,interruptLead:Bu,listSubtaskJobs:Kr,cancelSubtask:Wu,readEvents:D,appendEvent:R,knownAgents:ti}),GC(),wo().then(t=>{t||console.warn(`[boot] no host tmux server on ${Ut} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),bk(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Ii=ub({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||vd.hostname(),name:process.env.BOSTRAT_NODE_NAME||vd.hostname(),listTracks:rt,detectAgents:Cu}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),sr().catch(()=>{}),km().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Xm().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),qp().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),Jg().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),ih().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{Ii?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function GC(){VC(),la().catch(()=>{}),setInterval(()=>la().catch(()=>{}),1e4)}function VC(){for(let t of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-runbook","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let e=ct.resolve(Lk,"..","bin",t),n=ct.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Lt.existsSync(e))continue;Lt.mkdirSync(n,{recursive:!0});let r=ct.join(n,t);(Lt.existsSync(r)?Lt.readlinkSync(r):null)!==e&&(Lt.rmSync(r,{force:!0}),Lt.symlinkSync(e,r)),console.log(`[bostrat-agent] ${t} CLI linked at ${r}`)}catch(e){console.error(`[bostrat-agent] ${t} CLI link failed:`,e?.message||e)}}function zC(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await rt(),r=await Tb({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 JC(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await rt();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await Mu(n.id).catch(()=>{}),await Cg(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}