bostrat-node 0.2.14 → 0.2.16

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 +35 -35
  2. package/dist/index.js +145 -141
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,121 +1,121 @@
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(`
1
+ var rx=Object.defineProperty;var Od=(t,e)=>{for(var n in e)rx(t,n,{get:e[n],enumerable:!0})};import lt from"node:path";import Lt from"node:fs";import tM from"node:readline";import{fileURLToPath as eM}from"node:url";import Se from"express";import nM from"node:http";import rM from"node:https";import Id from"node:os";import{WebSocketServer as oM}from"ws";import{execFile as S0}from"node:child_process";import{promisify as v0}from"node:util";import Us from"node:fs";import{randomUUID as T0}from"node:crypto";import Ft from"node:fs/promises";import Pd from"node:os";import Ct from"node:path";import{execFile as cx}from"node:child_process";import{promisify as lx}from"node:util";import{createHash as ox,randomBytes as sx,timingSafeEqual as ax}from"node:crypto";var Es="BOSTRAT_HOOK_TOKEN",_s="x-bostrat-hook";function Cd(){return sx(32).toString("hex")}function oo(t){let e=typeof t=="string"?t.trim():"";return e?ox("sha256").update(e).digest("hex"):null}function Md(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:ax(Buffer.from(t),Buffer.from(e))}var Pi=new Map,Bi=new Map,ix=6e4;function Is(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(Pi.set(t,(Pi.get(t)||0)+1),Bi.has(t)&&n-Bi.get(t)<ix)return!1;Bi.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=${Pi.get(t)}${o?` ${o}`:""}`),!0}async function Nd(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 ux(){return process.env.CLAUDE_CONFIG_DIR?Ct.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Ct.join(Pd.homedir(),".claude.json")}function dx(){return process.env.CLAUDE_CONFIG_DIR?Ct.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Ct.join(Pd.homedir(),".claude","settings.json")}async function Ge(t){if(!t||typeof t!="string")return;let e=Ct.resolve(t),n=ux(),r=dx(),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 "${_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 Ft.mkdir(Ct.dirname(r),{recursive:!0}),await Ft.writeFile(r,JSON.stringify(i,null,2)),await fx(e)}async function fx(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 px(e,["/.claude/settings.local.json"])}catch{}}async function px(t,e){try{let{stdout:n}=await lx(cx)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Ct.resolve(t,n.trim()),o=await Ft.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
2
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 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*$/,"")}
6
+ `)}catch{}}import Di from"node:fs/promises";import mx from"node:os";import $s from"node:path";function hx(){let t=process.env.CODEX_HOME||$s.join(mx.homedir(),".codex");return $s.join(t,"config.toml")}function Bd(t){return`[projects."${t}"]`}function wx(t,e){let n=Bd(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 rr(t){if(!t||typeof t!="string")return;let e=$s.resolve(t),n=hx(),r="";try{r=await Di.readFile(n,"utf8")}catch{}if(wx(r,e))return;let o=Bd(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(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(`
13
+ `:"")}try{await Di.mkdir($s.dirname(n),{recursive:!0}),await Di.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Dd from"node:fs/promises";import gx from"node:os";import so from"node:path";function yx(t){let n=so.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||so.join(gx.homedir(),".cursor");return so.join(r,"projects",n)}async function or(t){if(!t||typeof t!="string")return;let e=so.resolve(t),n=yx(e),r=so.join(n,".workspace-trusted");await Dd.mkdir(n,{recursive:!0}),await Dd.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
14
+ `,"utf8")}import B from"node:fs/promises";import Kx from"node:os";import X from"node:path";import{execFile as Gx}from"node:child_process";import{promisify as Vx}from"node:util";import Rx from"node:fs";import Ld from"node:fs/promises";import io from"node:path";import $x from"node:os";import{fileURLToPath as Ox}from"node:url";import{execFile as Cx}from"node:child_process";import{promisify as Mx}from"node:util";import Li from"node:fs/promises";import Ve from"node:path";import{execFile as bx}from"node:child_process";import{promisify as kx}from"node:util";var xx=kx(bx),jd=process.env.HOME||"/home/globo",Sx=Ve.join(process.env.BOSTRAT_STATE_DIR||Ve.join(jd,".bostrat"),"repos"),vx=[...(process.env.BOSTRAT_REPOS_ROOT||Ve.join(jd,"Documents")).split(","),Sx].map(t=>t.trim()).filter(Boolean),Tx=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,Ax=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function Ex(t){try{return(await Li.stat(Ve.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function _x(t){let e=i=>Li.readFile(Ve.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 Ex(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:Ve.basename(o),branch:await _x(o)});return}if(s>=Tx)return;let i;try{i=await Li.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||Ax.has(c.name)||await e(Ve.join(o,c.name),s+1)}for(let o of vx)await e(Ve.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 Ix(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var ji=new Map;async function tt(t){if(!t)return null;if(ji.has(t))return ji.get(t);let e=null;try{let{stdout:n}=await xx("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=Ix(n)}catch{e=null}return ji.set(t,e),e}async function ao(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 Fd=Mx(Cx),Nx=process.env.BOSTRAT_STATE_DIR||io.join(process.env.HOME||$x.tmpdir(),".bostrat"),Px=io.join(Nx,"repos"),Ud=/^[^/\s]+\/[^/\s]+$/,Bx=5*6e4,Dx=6e4,jx=10*6e4,Hd=fetch,Fi=new Map,En=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 Os(t=process.env){return!!(zt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Wd=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),sr=(t,e)=>Object.assign(new Error(t),{status:e});async function Cs(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Ud.test(n))throw sr("repo must be owner/name",400);let r=Fi.get(n);if(r&&r.expiresAt-Date.now()>Bx)return r;if(!Os(e))throw sr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Hd(`${zt(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Wd(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw sr(`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 sr(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 Fi.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&&En&&Date.now()-En.at<Dx)return En.data;try{let n=await Hd(`${zt(t)}/v1/github/repos`,{headers:Wd(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return En={at:Date.now(),data:r},r}catch(n){return En?En.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function qd(){Fi.clear(),En=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 Cs(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Ui({env:t=process.env}={}){return(await co({env:t})).username??null}var Lx=()=>Ox(new URL("../bin/bostrat-git-credential",import.meta.url)),Fx=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Ux(t){let[e,n]=String(t).split("/");return io.join(Px,e,n)}async function Hi(t,{env:e=process.env}={}){let n=(...r)=>Fd("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Lx()}" ${Fx(e)}`),await n("config","credential.useHttpPath","true")}async function Wi(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Ud.test(String(t||"")))throw sr(`bad repo slug "${t}"`,400);let r=Ux(t);if(Rx.existsSync(io.join(r,".git")))return r;let{token:o}=await Cs(t,{env:e});await Ld.mkdir(io.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Fd("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:jx,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Ld.rm(r,{recursive:!0,force:!0}).catch(()=>{}),sr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Hi(r,{env:e}),r}var Hx=6e4,Wx=5e3,qi=new Map,qx=(...t)=>fetch(...t);function Kd(t=process.env){return!!(zt(t)&&t.BOSTRAT_PLANE_TOKEN)}var Gd=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Vd(t,e,n){let r=zt(n),o=new AbortController,s=setTimeout(()=>o.abort(),Wx);try{return await qx(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function zd(t,{env:e=process.env,force:n=!1}={}){if(!Kd(e))return{facts:[],source:"unconfigured"};let r=qi.get(t);if(!n&&r&&Date.now()-r.at<Hx)return{facts:r.facts,source:"cache"};try{let o=await Vd(`/v1/memory/${encodeURIComponent(t)}`,{headers:Gd(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return qi.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 Ms(t,e,{env:n=process.env}={}){if(!Kd(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await Vd(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Gd(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 qi.delete(t),{pushed:s.filter(a=>a.outcome==="accepted").length,rejected:s.filter(a=>a.outcome==="rejected"),source:"plane"}}catch(r){return{pushed:0,source:"unreachable",error:r?.message}}}var zx=Vx(Gx),Ki=process.env.BOSTRAT_STATE_DIR||X.join(process.env.HOME||Kx.tmpdir(),".bostrat"),Jd=200,Jx=25*1024;function Ns(){return X.join(Ki,"memory","repos")}var Te="operator";function _n(t){return t===Te?X.join(Ki,"memory",Te):X.join(Ns(),t)}var Qd=".tombstones.json";async function lo(t){try{return JSON.parse(await B.readFile(X.join(t,Qd),"utf8"))}catch{return{}}}async function Gi(t,e){let n=await lo(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,Qd),JSON.stringify(n))}function Xd(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 Yd(t){try{let{stdout:e}=await zx("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function Vi(t){if(!t)return[];let e=Xd(await Yd(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=Xd(await Yd(X.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 ze(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}=Ps(n),o=ze(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 tf(t,e){let n=ze(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=ze(t,"trust");if(!o||/^operator/i.test(o))return Te}return e}var ar=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function zi(t,e,n,r=new Map){let o=(t||"").split(`
15
15
  `),s=new Map;for(let c of(e||"").split(`
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(`
16
+ `)){let l=ar(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=ar(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 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(`
19
+ `)}function ef(t,e){let n=ze(e,"name")||t.replace(/\.md$/,""),r=ze(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function uo(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 nf(t,e){let n=X.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=X.join(n,l),f;try{f=await B.readFile(u,"utf8")}catch{continue}let{fm:d}=Ps(f),p=tf(d,e);if(!p)continue;let m=_n(p),g=X.join(m,l),y=await ie(u,f);if(i.has(m)||i.set(m,await lo(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,ef(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,zi(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Xx(){let t=X.join(Ki,"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 Vi(s),c=await nf(s,i||null);n+=c.harvested;for(let[l,u]of c.changedByKey){r.has(l)||r.set(l,new Set);for(let f of u)r.get(l).add(f)}}return{harvested:n,changedByKey:r}}var Yx=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,Zx=t=>{let e=new Map;for(let n of(t||"").split(`
20
+ `)){let r=ar(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function Qx(t,e){let n=_n(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(!Yx.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 Gi(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=zi(l,[...s.values()].join(`
21
21
  `),o);if(a.length){let f=new Set(a);u=u.split(`
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
22
+ `).filter(d=>!f.has(ar(d))).join(`
23
+ `)}await B.mkdir(n,{recursive:!0}),await B.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Ji(t,e=null){let n=_n(t),r=e?.length?e:await uo(n);if(!r.length)return{pushed:0,source:"empty"};let o=Zx(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 Ms(t,s)}async function t0(t){let{facts:e,source:n}=await zd(t);e.length?await Qx(t,e):n==="plane"&&await Ji(t)}function e0(t){let e=t.split(`
24
+ `);e.length>Jd&&(e=e.slice(0,Jd));let n=e.join(`
25
+ `);for(;Buffer.byteLength(n,"utf8")>Jx&&e.length;)e.pop(),n=e.join(`
26
+ `);return n}var n0=`# 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=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}
31
+ `;async function r0(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=_n(u),d=await lo(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 uo(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
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=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(`
36
+ `,c=e0(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"),n0,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var Zd=0,o0=3e4;async function rf(t){try{let e=await Vi(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,Te];if(Date.now()-Zd>o0){Zd=Date.now(),await s0();let{changedByKey:r}=await Xx();for(let[o,s]of r)await Ji(o,[...s])}for(let r of n)await t0(r);return await r0(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function s0(){let t=0,e=[];try{e=await B.readdir(Ns(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=X.join(Ns(),n.name),o=_n(Te),s=new Set,a=new Map;for(let p of await uo(r)){let m=X.join(r,p),g;try{g=await B.readFile(m,"utf8")}catch{continue}let{fm:y}=Ps(g);if(tf(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,ef(p,y)),t++}if(!s.size)continue;let i=Date.now();await Gi(r,[...s].map(p=>[p,i])),await Ms(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(ar(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,zi(d,l,s,a))}return{moved:t}}async function of(){let t=[],e=[Te];try{for(let n of await B.readdir(Ns(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=_n(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 B.readFile(X.join(r,i),"utf8").catch(()=>""),{fm:l}=Ps(c);a.push({file:i,name:ze(l,"name")||i.replace(/\.md$/,""),description:ze(l,"description")||"",scope:ze(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 sf(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=_n(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 Gi(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=>ar(l)!==e).join(`
40
+ `)).catch(()=>{});let c=await Ms(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function af(t){try{let e=await Vi(t),{harvested:n,changedByKey:r}=await nf(t,e[0]||null);for(let[o,s]of r)await Ji(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import Qi from"node:fs/promises";import wf from"node:path";import uf from"node:fs/promises";import df from"node:os";import ff from"node:path";import Xi from"node:fs/promises";import a0 from"node:path";var i0=0;async function Mt(t,e,{mode:n}={}){await Xi.mkdir(a0.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++i0}.tmp`;await Xi.writeFile(r,JSON.stringify(e,null,2)+`
41
+ `,n!=null?{mode:n}:void 0),await Xi.rename(r,t)}var c0=1e4,l0=3e4,cf=2e3,u0=()=>process.env.BOSTRAT_STATE_DIR||ff.join(process.env.HOME||df.tmpdir(),".bostrat"),pf=()=>ff.join(u0(),"content-outbox.json"),d0=()=>process.env.BOSTRAT_NODE_ID||df.hostname(),f0=(...t)=>fetch(...t),p0=l0;function pt(t=process.env){return!!(zt(t)&&t.BOSTRAT_PLANE_TOKEN)}var po=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function mo(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),c0);try{return await f0(`${zt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var Ds=null,Yi=null,Bs=null,fo=null;async function mf(){return Ds||(Yi||=uf.readFile(pf(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>Ds={ops:Array.isArray(t?.ops)?t.ops:[]}).finally(()=>Yi=null),Yi)}async function hf(){await Mt(pf(),Ds).catch(()=>{})}function lf(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 mf(),n=lf(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=>lf(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>cf&&(e.ops=e.ops.slice(-cf)),await hf()}function m0(){Bs||(Bs=setTimeout(()=>{Bs=null,ir().catch(()=>{})},p0),Bs.unref?.())}async function h0(t,e=process.env){if(t.op==="record"){let n=await mo(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...po(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:d0()}]})},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 uf.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await mo(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...po(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw 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 mo(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:po(e)},e);if(!n.ok)throw new Error(`plane ${n.status}`);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function ir({env:t=process.env,log:e=console}={}){return pt(t)?fo||(fo=(async()=>{let n=await mf(),r=0;for(;n.ops.length;){let o=n.ops[0],s;try{s=await h0(o,t)}catch(a){return m0(),{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 hf()}return{flushed:r,pending:0}})().finally(()=>{fo=null}),fo):{flushed:0,source:"unconfigured"}}async function Rt(t,e,{trackId:n=null,env:r=process.env}={}){return pt(r)?(await Zi({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),ir({env:r})):{queued:!1,source:"unconfigured"}}async function Je(t,e,n,r,o,{env:s=process.env}={}){return pt(s)?(await Zi({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),ir({env:s})):{queued:!1,source:"unconfigured"}}async function Qt(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return pt(r)?(await Zi({op:"delete",family:t,id:e,deletedAt:n}),ir({env:r})):{queued:!1,source:"unconfigured"}}async function Tt(t,{env:e=process.env}={}){if(!pt(e))return{records:[],source:"unconfigured"};try{let n=await mo(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:po(e)},e);if(!n.ok)throw new Error(`plane ${n.status}`);let r=await n.json();return{records:Array.isArray(r?.records)?r.records:[],source:"plane"}}catch(n){return{records:[],source:"unreachable",error:n?.message}}}async function js(t,e,n,r,{env:o=process.env}={}){if(!pt(o))return null;try{let s=await mo(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:po(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="png"?a:a.toString("utf8")}catch{return null}}var w0=".bostrat/handoff",g0="latest.md",y0=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null;async function gf(t,e,{box:n=null,agent:r=null}={}){if(!t||!pt())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 Je("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function yf(t){try{let e=y0(t);if(!e||!pt())return{restored:!1};let n=wf.join(t,w0,g0);if(await Qi.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await Tt("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 js("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Qi.mkdir(wf.dirname(n),{recursive:!0}),await Qi.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import ho from"node:fs/promises";import b0 from"node:os";import kf from"node:path";var k0=[/[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 wo(t){let e=String(t??"");for(let n of k0)e=e.replace(n,"\u2039redacted\u203A");return e}var tc=process.env.BOSTRAT_DEBUG_DIR||kf.join(b0.homedir(),".bostrat","debug"),cr=kf.join(tc,"handoff.jsonl"),bf=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function Ls(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function x0(t){try{await ho.mkdir(tc,{recursive:!0}),await ho.appendFile(cr,`${JSON.stringify(t)}
42
+ `,"utf8");let n=(await ho.readFile(cr,"utf8")).split(`
43
+ `).filter(Boolean);n.length>bf&&await ho.writeFile(cr,`${n.slice(-bf).join(`
44
44
  `)}
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,`
45
+ `,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${cr}: ${e?.message||e}`)}}async function U(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&&Ls())try{e.paneTail=wo((await ft(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(Ls()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await x0(e)}async function xf({limit:t=80}={}){try{return(await ho.readFile(cr,"utf8")).split(`
46
+ `).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=wo(o.paneTail)),o}catch{return{raw:wo(r)}}})}catch{return[]}}var Sf={DEBUG_DIR:tc,LOG_FILE:cr};var nt=v0(S0);function lr(t){return new Promise(e=>setTimeout(e,t))}function Fs(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";function Tf(t){return`/tmp/tmux-${t}/default`}function A0({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=Tf(r);return e?o:n?`${n}/bostrat/tmux-default`:o}var vf=typeof process.getuid=="function"?process.getuid():0,Ut=A0({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return Us.existsSync(Tf(vf))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:vf}),E0=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",ec=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",_0="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";function nc(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${_0}=1`]}var rc="bostrat-usage-probe",I0=new Set([rc]);function oc(t,e){let n=In(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function In(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function Xe(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function R0(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:In(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function sc(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 nt(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function Hs(t){return sc(String(t||"").trim().split(/\s+/)[0])}var ac={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 Ye(){if(!Us.existsSync(Ut))return!1;try{return await nt(N,P("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function go(){if(await Ye())return!0;try{Us.mkdirSync(Ut.replace(/\/[^/]+$/,""),{recursive:!0}),await nt(N,P("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Ye()}var $0=/^[A-Za-z0-9_.-]{1,64}$/;function wt(t){return typeof t=="string"&&$0.test(t)}async function Rn(){if(!Us.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 nt(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=>!I0.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function Z(t){return(await Rn()).find(n=>n.name===t)||null}var O0=/^[A-Za-z0-9 ._↻-]+$/,C0=64;function M0(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=C0&&O0.test(e)}function ic(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Af(t){try{let{stdout:e}=await nt(N,P("list-sessions","-F","#{session_name}"));return e.split(`
48
+ `).map(n=>n.trim()).includes(t)}catch{return!1}}var N0=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,P0=1e3,B0=/not found|Unknown command/i;function D0(t){let e=String(t||"").trim().split(/\s+/)[0]||"";if(e.includes("/"))return null;let n=[];for(let o=1;o<=e.length-3;o++)n.push(e.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function j0(t,e){let n=Date.now()+N0(),r=D0(e),o=!1;for(;;){if(await lr(P0),!await Af(t)||!await ur(t))return;let s=await ft(t,{lines:15});if(!o&&B0.test(s)&&r?.test(s)){o=!0,await U({flow:"spawn",phase:"launch-retry",sessionName:t,agent:Xe(e),promptHead:e.slice(0,120)}).catch(()=>{}),await nt(N,P("send-keys","-t",t,"C-u")),await nt(N,P("send-keys","-t",t,"-l",e)),await nt(N,P("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await U({flow:"spawn",phase:"launch-failed",sessionName:t,agent:Xe(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function $n(t,e=E0,n={}){if(!M0(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=ic(r);if(!wt(o))throw new Error("resulting tmux session name is invalid");if(!await go())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 Af(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||ec,a=n.agent||Xe(s),i=!!n.resumeSessionId,c=n.resumeSessionId||T0(),l=i?s:oc(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&In(s),d=n.firstRun===!0;d||(f&&await Ge(e),a==="codex"&&await rr(e),a==="cursor"&&await or(e),await rf(e),await yf(e));let p=f&&!d?Rs():null,m=Cd(),g=["-e",`${Es}=${m}`];p&&g.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&g.push(...nc());try{await nt(N,P("new-session","-d","-s",o,"-c",e,...g))}catch(y){throw Fs(p?Fs(y,p):y,m)}return await On(o),await nt(N,P("set-option","-t",o,"@bostrat-session","1")),await nt(N,P("set-option","-t",o,"@bostrat-title",r)),await nt(N,P("set-option","-t",o,"@bostrat-sid",c)),await nt(N,P("set-option","-t",o,"@bostrat-agent",a)),await nt(N,P("send-keys","-t",o,l,"Enter")),R0(l)&&j0(o,l).catch(()=>{}),{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 nt(N,P("kill-session","-t",t))}async function Ef(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 nt(N,n)}catch{}}async function ft(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 nt(N,n);return String(r||"")}catch{return""}}var L0=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,F0=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,xt=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,U0=Number(process.env.BOSTRAT_BOX_COLS)||120,H0=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function _f(t){if(!wt(t))return 0;try{let{stdout:e}=await nt(N,P("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
49
+ `).filter(Boolean).length}catch{return 0}}async function On(t,{cols:e=U0,rows:n=H0}={}){if(wt(t)&&!await _f(t))try{let r=await If(t);if(r.cols===e&&r.rows===n)return;await nt(N,P("resize-window","-t",t,"-x",String(e),"-y",String(n))),await nt(N,P("set-window-option","-t",t,"window-size","latest")),await lr(80)}catch{}}async function W0(t){if(!wt(t))return 0;try{let{stdout:e}=await nt(N,P("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function ur(t){let e=await W0(t);if(!e)return!1;try{return await nt("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function If(t){if(!wt(t))return{cols:0,rows:0};try{let{stdout:e}=await nt(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 Ws(t,{cols:e=L0,rows:n=F0}={}){if(wt(t)&&!await _f(t))try{let r=await If(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await nt(N,P("resize-window","-t",t,"-x",String(o),"-y",String(s))),await nt(N,P("set-window-option","-t",t,"window-size","latest")),await lr(80))}catch{}}async function Ze(t){return await Ws(t),ft(t,{lines:xt})}var q0=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=>q0.has(s));for(let s=0;s<o.length;s++)await nt(N,P("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await lr(n);return U({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function qs(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 nt(N,P("send-keys","-t",t,"-N",String(r),"BSpace")),U({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Ks(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 nt(N,P("send-keys","-t",t,"-l",s)),n>0&&await lr(n);U({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function St(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 nt(N,P("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await lr(s),await nt(N,P("send-keys","-t",t,"Enter"))}U({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 K0 from"node-pty";async function $f(t,e,n={}){if(!wt(e)){Gs(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await Ye()){Gs(t,`host tmux server not running on ${Ut}`),t.close(1011,"tmux server unavailable");return}if(!await Z(e)){Gs(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=Rf(n.cols,120,Of),o=Rf(n.rows,32,Cf),s;try{s=K0.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){Gs(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&cc(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(cc(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"&&G0(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{}On(e).catch(()=>{})})}var Of=20,Cf=6;function G0(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=Of&&Number(e)>=Cf}function Rf(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function cc(t,e){try{t.send(JSON.stringify(e))}catch{}}function Gs(t,e){cc(t,{type:"error",message:e})}function Mf(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 yo from"node:fs";import V0 from"node:fs/promises";import dr from"node:path";import z0 from"node:os";var Nf={"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},J0=2e5,lc=process.env.BOSTRAT_CLAUDE_PROJECTS||dr.join(z0.homedir(),".claude","projects");function uc(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function dc(t){let e=dr.join(lc,uc(t)),n;try{n=yo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=dr.join(e,o);try{let{mtimeMs:a}=yo.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=dr.join(lc,uc(t),`${e}.jsonl`);return yo.existsSync(n)?n:null}function Pf(t){if(!t)return[];let e=dr.join(lc,uc(t)),n;try{n=yo.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=yo.statSync(dr.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 V0.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 X0(t){let e=t.split(`
50
+ `);for(let n=e.length-1;n>=0;n--){let r=e[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function bo(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=Ae(t,e),o=!0,!r)return null}else if(n){if(r=dc(t),o=!1,!r)return null}else return null;let s;try{s=X0(await Nt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Nf,s.model),i=Nf[s.model]||J0;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 Y0(t,e){return!t||!e?null:Ae(t,e)}function Z0(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 Q0(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 tS(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function fc(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 Bf(t,e){if(!t.uuid)return null;let n=fc(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 eS=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function pc(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"&&eS.test(g)&&s.set(m.id,g.split("/").pop())}if(tS(l.content)){let m=Bf(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=Z0(l.content),d=u==="assistant"?Q0(l.content):null,p=u==="user"?Bf(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 nS=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function Df(t,e,{turns:n=16}={}){let r=Y0(t,e);if(!r)return"";let o=await pc(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}:** ${nS(s.text||c,700)} ${i}`.trim()}).join(`
53
+
54
+ `):""}import ZS from"node:fs";import hr from"node:fs/promises";import wr from"node:path";import rS from"node:fs/promises";import{execFile as oS}from"node:child_process";import{promisify as sS}from"node:util";var mc=sS(oS),aS=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,jf=new Set,Lf=Promise.resolve(),hc=null;function Uf(t){hc=typeof t=="function"?t:null}function iS(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function Hf({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||jf.has(r)||(jf.add(r),Lf=Lf.then(()=>cS({sessionName:t,from:e,to:r})).catch(()=>{}))}async function cS({sessionName:t,from:e,to:n}){await U({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await mc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await rS.realpath(r),s=iS(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await mc("npm",["install","-g","@openai/codex"],{env:a,timeout:aS});let i=(await mc(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await U({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await Ff({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await U({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await Ff({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function Ff(t){if(hc)try{await hc(t)}catch{}}var lS=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Wf=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function uS(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
55
+ `);if(!Wf.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(lS);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"&&Wf.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 Vs=/(^|\n)\s*›/,Gf=/^\s*Welcome to Codex\b/;function fr(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
56
+ `);for(let r=n.length-1;r>=0;r--)if(Gf.test(n[r]))return n.slice(r).join(`
57
+ `);return e}function dS(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 fS(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 qf=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function pS(t,e=""){let n=fr(t);if(Vs.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(qf);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(qf.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&&!Gf.test(r[l])&&dS(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=fS(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Kf=/Enter this one-time code/,mS=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function hS(t,e=""){let n=fr(t);if(Vs.test(n)||!Kf.test(n))return null;let r=n.split(`
59
+ `),o=r.findIndex(i=>Kf.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(mS);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 wS(t,e=""){let n=fr(t);return Vs.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 gS(t,e=""){let n=fr(t);return Vs.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 wc=/Run Everything|Add a follow-up|Plan, search, build anything/;function yS(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||wc.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 bS=/click this link to log in|Signing in with the browser/;function kS(t,e=""){let n=String(t);if(!bS.test(n)||wc.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 xS(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||wc.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 Vf(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=hS(e,n)||wS(e,n)||gS(e,n)||pS(e,n);if(r)return r}if(t==="cursor"){let r=kS(e,n)||xS(e,n)||yS(e,n);if(r)return r}return uS(e,{provider:t,box:n})}function zf(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 SS=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,gc=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ko=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/}},vS=/Trust this workspace|\[a\]\s*Trust/,TS=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function AS(t){let e=String(t||"");if(!At.codex.block.test(e))return null;let n=fr(e);if(!At.codex.block.test(n))return null;let r=n.match(TS);return{from:r?.[1]||null,to:r?.[2]||null}}function Qe(t){return new Promise(e=>setTimeout(e,t))}var ES=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,zs=new Map;function Jf(t,e){let n=Date.now(),r=ES();for(let[s,a]of zs)n-a>=r&&zs.delete(s);let o=`${t}::${e}`;return zs.has(o)?!1:(zs.set(o,n),!0)}var _S=68,IS=/^\s*[─━]{10,}\s*$/;function RS(t){let e=0;for(let n of String(t).split(`
62
+ `))IS.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function yc(t,e){let n=At[t];if(!n?.busy)return"idle";if(bc(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=RS(e);return t==="claude"&&r>0&&r<_S?"unknown":"idle"}var $S=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,OS=15;function Yf(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
63
+ `).slice(-OS).join(`
64
+ `);return $S.test(n)}function Ee(t,e){return yc(t,e)==="busy"}function Js(t,e){return yc(t,e)!=="idle"}function Xs(t,e){let n=At[t];return n?.up?n.up.test(e):!0}function CS(t,e){let n=At[t];return n?bc(t,e)?!1:n.up.test(e)&&yc(t,e)==="idle":!0}function bc(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 Xf(t,e,n){let r=Date.now()+n,o=0;for(await On(t).catch(()=>{});;){let s=await ft(t);if(CS(e,s)){await Qe(ko);return}if(e==="cursor"&&vS.test(s)&&!At.cursor.up.test(s)&&o<3){if(!Jf(t,"cursor-trust")){await Qe(gc);continue}o+=1,await U({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await St(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await Qe(ko);continue}if(e==="codex"&&o<3){let a=AS(s);if(a){if(!Jf(t,`codex-update:${a.from}->${a.to}`)){await Qe(gc);continue}o+=1,await U({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await St(t,"3",{enter:!1,kind:"codex-update-skip"}),Hf({sessionName:t,from:a.from,to:a.to}),await Qe(ko);continue}}if(Date.now()>r){if(bc(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'),U({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await ur(t)){let a=`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw U({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),U({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 Qe(ko);return}await Qe(gc)}}function Zf({agent:t,agentCmd:e}={}){let n=e?Xe(e):Xe(t||"");return{label:n,realClaude:e?In(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 Ys({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Zf({agent:n,agentCmd:r});return!e||!o&&!s?null:tn({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function tn({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=SS()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Zf({agent:r,agentCmd:o});if(i&&t&&await Ge(t),c&&t&&await rr(t),l&&t&&await or(t),(i||c||l)&&n){await Xf(n,a,s);return}let u=At[r]?r:At[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Xf(n,u,s);return}await Qe(ko)}var MS=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),NS=(()=>{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:MS}:t==="claude"?{enter:!0,submitDelayMs:NS}:{enter:!0}}import Qs from"node:fs/promises";import op from"node:path";var PS=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],BS={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 pr(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of PS){let o=0;for(let s of BS[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var DS=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),jS=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Qf(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))DS.has(r)&&(e+=o||0),jS.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function xo(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 tp=1.5,ep=.75,LS=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 rp(t){let e=xo(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||LS.has(r)||n.push(r);return n}function FS(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(np(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(np(n,"assistant",o))}}return e}function np(t,e,n){let r=pr(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 So(t){let e=FS(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=rp(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 Zs(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(rp(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+tp*(1-ep+ep*(y/(t.avgdl||1))),S=p*(g*(tp+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 sp="Relevant prior discussion (verbatim, retrieved";async function US(t){let e=op.join(t,".bostrat","events"),n;try{n=await Qs.readdir(e)}catch{return So([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Qs.readFile(op.join(e,o),"utf8");for(let a of s.split(`
65
+ `))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return So(r)}function HS(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 WS(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Zs(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?["",`## ${sp}, 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 ap(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Qs.readFile(e,"utf8");if(a.includes(sp))return{appended:!1,reason:"already-enriched"};let i=await US(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=WS(i,HS(a),{k:r,maxChars:o,exclude:s});return c?(await Qs.appendFile(e,`
68
68
  `+c),{appended:!0,chars:c.length,chunks:c.split(`
69
- - `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}import 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(/�+$/,"")}
69
+ - `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}import kc from"node:fs/promises";import mr from"node:path";import qS from"node:os";import{EventEmitter as KS}from"node:events";import{randomUUID as GS}from"node:crypto";var VS=new Set(["session.ready","turn.user","turn.agent","turn.retracted","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","context.usage","cost.recorded","memory.seed","handoff.artifact","code.pushed","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","notify.raised","provider.connected","track.summary.recorded","changelog.published","stall.detected","error"]),zS=new Set(["user","agent","orchestrator","system"]),JS=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Jt=new KS;Jt.setMaxListeners(0);var xc=process.env.BOSTRAT_STATE_DIR||mr.join(process.env.HOME||qS.tmpdir(),".bostrat");function ea(t){return t?.cwd?mr.join(t.cwd,".bostrat","events"):mr.join(xc,"events")}var ip=(t,e)=>mr.join(t,`${e}.ndjson`),ta=new Map;async function XS(t,e){if(!ta.has(e)){let o=(await D(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);ta.set(e,o)}let n=ta.get(e)+1;return ta.set(e,n),n}function YS(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||!VS.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!zS.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!JS.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?mr.join(xc,"events"):ea(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=YS(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await XS(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||GS(),trackId:n,boxId:e.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:e.ts??Date.now(),type:e.type,actor:e.actor??"system",provider:e.provider??null,source:e.source??"orchestrator",payload:e.payload??{},...e.causationId?{causationId:e.causationId}:{},...e.correlationId?{correlationId:e.correlationId}:{}};return await kc.mkdir(r,{recursive:!0}),await kc.appendFile(ip(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"?mr.join(xc,"events"):ea(t));let o;try{o=await kc.readFile(ip(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 cp(t,e){let n=`track:${t}`;return Jt.on(n,e),()=>Jt.off(n,e)}var fp=".bostrat/handoff",pp="latest.md",lp=32*1024;function QS(t){let e=wo(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=lp?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,lp).toString("utf8").replace(/�+$/,"")}
72
72
 
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}
73
+ [\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var mp="Checkpoint: write a work-in-progress handoff",hp="HANDOFF WRITTEN",wp="Resume from .bostrat/handoff/latest.md",tv="/clear",ev=t=>`${mp} 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 ${hp} and take no further action.`,nv=`${wp}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,gp="Beat: write a",yp="BEAT WRITTEN",rv=".bostrat/arc",ov="beat.txt",up=200,bp=4,kp=5,sv=36,av=t=>`${gp} 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 ${bp} further lines, each starting with "- " and naming ONE piece in at most ${kp} 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 ${yp} and take no further action.`,dp=/^(?:[-–—*•·]|\d+[.)])\s+/,iv=(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 cv(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(dp.test(s)){if(r.length>=bp)continue;let a=iv(s.replace(dp,"").replace(/[.;,:]+$/,""),kp,sv);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>up&&(o=`${o.slice(0,up-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function xp(t,e,{timeoutMs:n=9e4}={}){let r=wr.join(e,rv),o=wr.join(r,ov);await hr.mkdir(r,{recursive:!0});let s=await na(o),a=await Z(t);if(!a)throw new Error(`session "${t}" not found`);return await ra(t,a,av(o)),await Rp(o,s,n),cv(await hr.readFile(o,"utf8"))}function Sp(t,e){let n=(e||"").trim();return n?t==="assistant"?n===hp||n===yp:n.startsWith(mp)||n===tv||n.startsWith(wp)||n.startsWith(gp):!1}var lv=6e5;function vp(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-lv;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 Tp(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 Ap(t,{windows:e=[]}={}){if(Sp(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 uv=3500,dv=3e5,Sc=new Map;function oa(t){return Sc.get(t)||null}async function Ep(t){let e=Sc.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:wr.join(n.cwd,fp,pp)};return Sc.set(t,r),fv(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function fv(t,e){t.step="summarize",await _p(t.name,e),t.step="clear";let n=await Z(t.name);await ra(t.name,n,"/clear"),await $p(uv),t.step="resume",await ra(t.name,n,nv),t.step="done",t.finishedAt=Date.now()}async function _p(t,e){let n=wr.join(e,fp),r=wr.join(n,pp);await hr.mkdir(n,{recursive:!0});let o=await na(r),s=await Z(t);await U({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await ra(t,s,ev(r)),await Rp(r,o,dv),await U({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await na(r)});let a=await ap(e,r,{exclude:l=>Sp(l.role,l.text)});await U({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 hr.readFile(r,"utf8");await R({id:i,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...QS(l),ts:Date.now()}})}catch{}}await af(e);let c=await pv(n,r);return i&&await gf(i,c||r,{box:t,agent:s?.agent??null}),r}async function Ip(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 _p(t,e.cwd)}async function na(t){try{return(await hr.stat(t)).mtimeMs}catch{return 0}}async function Rp(t,e,n){let r=Date.now()+n;for(;;){if(await na(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await $p(2e3)}}async function pv(t,e){try{if(!ZS.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=wr.join(t,`${n}.md`);return await hr.copyFile(e,r),r}catch{return null}}function $p(t){return new Promise(e=>setTimeout(e,t))}async function ra(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 tn({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 St(t,n,{kind:"orchestration-drive",..._e(r.agent)})}import L from"node:fs/promises";import F from"node:path";import{randomUUID as cs}from"node:crypto";import{execFile as TI}from"node:child_process";import{promisify as AI}from"node:util";import hh from"node:path";var vc={"claude-opus-5":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"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}},Op={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 Cp(t){if(!t)return null;let e=String(t).trim();return vc[e]?e:Op[e]?Op[e]:Object.keys(vc).find(r=>e.startsWith(r))||null}var sa=t=>Cp(t)!==null;function le(t,e={}){let n=vc[Cp(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 Mp=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function mv(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function Np(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function ia(t){let e=t.payload?.model||"unknown",n=mv(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&&!sa(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=Mp();for(let f of n){let{usd:d,model:p,tokens:m,repriced:g,unpriced:y}=ia(f),b=f.provider||"unknown",S=f.boxId||"unattributed";if(g&&i++,y&&(c++,l.add(p)),a+=d,Np(u,m),r[p]||={usd:0,tokens:Mp(),events:0},r[p].usd+=d,r[p].events+=1,Np(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:vo(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:aa(r,f=>({...f,usd:vo(f.usd)})),byProvider:aa(o,f=>({...f,usd:vo(f.usd)})),byBox:aa(s,f=>({usd:vo(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:aa(f.epochs,d=>({usd:vo(d.usd),events:d.events,provider:d.provider,models:[...d.models]}))}:{}}))}}var vo=t=>Math.round(t*1e4)/1e4,aa=(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 To from"node:os";import{randomUUID as hv}from"node:crypto";import{execFile as wv}from"node:child_process";import{promisify as gv}from"node:util";import{chromium as yv}from"playwright-core";var Bp=gv(wv),Dp=process.env.BOSTRAT_STATE_DIR||Pt.join(process.env.HOME||To.tmpdir(),".bostrat"),jp=Pt.join(Dp,"mocks.json"),bv=process.env.BOSTRAT_MOCK_ENGINE||"chromium",kv=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",xv=process.env.BOSTRAT_FIREFOX_BIN||"firefox",Sv=1300,gr=2,vv=32,Lp=1500,yr=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,Tv=700*1024*1024;async function Av(){if(process.platform!=="darwin")return To.freemem();try{let{stdout:t}=await Bp("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:To.freemem()}catch{return To.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"}},$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(`
76
+ `)}`:e),this.name="MockRenderError"}},Cn=null,Tc=null;async function fe(){return Cn||(Tc||=(async()=>{try{Cn=JSON.parse(await kt.readFile(jp,"utf8"))}catch{Cn={mocks:[]}}finally{Tc=null}return Cn.mocks||=[],Cn})(),Tc)}async function de(){await Mt(jp,Cn)}var en=t=>Rt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Mn=(t,e,n)=>Je("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 ca=t=>Pt.join(t,".bostrat","mocks");function Ht(t,e){let n=ca(t.cwd);return{html:Pt.join(n,`${t.id}.v${e}.html`),png:Pt.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 Ec(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var Ev=Buffer.from([137,80,78,71,13,10,26,10]);function Fp(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(Ev)}async function _v(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 Iv({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 Rv(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 $v(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 Ov({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await yv.launch({executablePath:kv,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:gr})).newPage();a.setDefaultTimeout(yr),await a.goto(`file://${t}`,{waitUntil:"load",timeout:yr}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:yr});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await Rv(a,i),u=l&&Iv({...l,pad:vv*gr,align:gr});u&&(i=await $v(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Lp)throw new te(`capture produced a ${i.length}-byte image`);let c=Ec(i);if(!c)throw new te("output is not a PNG");return await kt.writeFile(e,i),{width:Math.round(c.width/gr),height:Math.round(c.height/gr),dpr:gr,renderMs:Date.now()-r}}catch(s){throw s instanceof te?s:new te(/timeout/i.test(s?.message||"")?`render timed out after ${yr}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function Pp({htmlPath:t,outPath:e,width:n}){let r=await kt.mkdtemp(Pt.join(To.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Bp(xv,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:yr,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new te(c?.killed?`render timed out after ${yr}ms`:"firefox exited non-zero",s)}let a=await kt.stat(e).catch(()=>null);if(!a||a.size<Lp)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=Ec(await _v(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 Cv(t){if(bv==="firefox")return Pp(t);try{return await Ov(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return Pp(t);throw e}}var Mv=Cv;var Ac=Promise.resolve();function Up(t){return Ac=Ac.then(t,t),Ac}async function Hp(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 Av();if(o<Tv)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 Mv({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}Wp(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}),en(r),Mn(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}),en(r)}}}function Wp(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 Nv({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?Sv:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Nn({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 nn(t){let n=(await fe()).mocks.find(r=>r.id===t);return n?{...n}:null}async function qp({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=Nv({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&&!Fp(r))throw new Error("image must be a PNG");let d=await fe(),p={id:hv().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(ca(o),{recursive:!0}),d.mocks.push(p),f?(await kt.writeFile(Ht(p,1).html,n),await de(),await ee(p,"created"),en(p),Mn(p,1,"html"),Up(()=>Hp(p.id,1))):(await Gp(p,1,r),await de(),await ee(p,"created"),await ee(p,"ready",{version:1,width:p.width,height:p.height,dpr:p.dpr}),en(p),Mn(p,1,"png")),{...p}}async function Kp(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&&!Fp(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(ca(s.cwd),{recursive:!0}),a?(s.state="queued",await kt.writeFile(Ht(s,i).html,e),await de(),await ee(s,"updated",{version:i}),en(s),Mn(s,i,"html"),Up(()=>Hp(s.id,i))):(await Gp(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}),en(s),Mn(s,i,"png")),{...s}}async function Gp(t,e,n){let r=Ec(n);if(!r)throw new Error("image must be a PNG");let o=Ht(t,e);await kt.writeFile(o.png,n),Wp(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 Vp(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 zp(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 Jp(t){let n=(await fe()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Pt.join(Dp,"mock-archive",t.id),o=ca(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(),en(i)}return await de(),n.length}async function Xp(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 Yp(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Tt("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await fe();for(let r of n.mocks){await en(r);for(let o of r.versions||[])o.htmlRel&&await Mn(r,o.v,"html"),o.pngRel&&await Mn(r,o.v,"png")}return{pushed:n.mocks.length,source:"plane"}}import Eo from"node:fs/promises";import Bt from"node:path";import Pv from"node:os";import Bv from"node:net";import{randomUUID as Dv}from"node:crypto";import{execFile as jv}from"node:child_process";import{promisify as Lv}from"node:util";var me=Lv(jv),Fv=process.env.BOSTRAT_STATE_DIR||Bt.join(process.env.HOME||Pv.tmpdir(),".bostrat"),nm=Bt.join(Fv,"views.json"),[Zp,Qp]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),rn=Number(process.env.BOSTRAT_VIEW_MAX)||3,Uv="bostrat-view-",br=t=>`${Uv}${t}`,tm=new Set(["bostrat","www","api","views","app"]),la=t=>Object.assign(new Error(t),{status:400}),Hv=new Set(["npm","pnpm","yarn","bun"]),Rc="package.json",Wv=/[|;&`]|\$\(/,qv=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Kv=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,rm=t=>String(t).trim().split(/\s+/).filter(Boolean),Gv=/^[A-Za-z_][A-Za-z0-9_]*=/;function Vv(t){for(let e of rm(t))if(!Gv.test(e))return e;return null}function om(t){let e=rm(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function zv(t,e){let n=om(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 Jv=t=>Eo.stat(t).then(e=>e.isDirectory(),()=>!1),sm=t=>Eo.stat(t).then(e=>e.isFile(),()=>!1),ua=t=>Eo.realpath(t).catch(()=>Bt.resolve(t));async function Xv(t,e){if(Bt.resolve(t)===Bt.resolve(e))return!0;let[n,r]=await Promise.all([ua(t),ua(e)]);return n===r}async function Yv(t){let n=(await Eo.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 sm(Bt.join(t,o.name,Rc))?`./${o.name}`:null))).filter(Boolean)}async function Zv(t,e){let[n,r]=await Promise.all([ua(t),ua(e)]),o=Bt.relative(n,r);if(o&&(o.startsWith("..")||Bt.isAbsolute(o)))throw la(`serve directory escapes the Track workspace: ${e}`)}async function Qv(t,e){if(!await Jv(t))throw la(`serve directory does not exist: ${t}`);let n=Vv(e);if(!n)return;if(!n.includes("/")&&!await sc(n,{loginShell:!0,failOpen:!0}))throw la(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(Wv.test(e)||qv.has(n)||!Hv.has(n)||!om(e).includes("run")||Kv.test(e))return;let r=zv(t,e);if(await sm(Bt.join(r,Rc)))return;let o=await Yv(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw la(`no ${Rc} in ${r}${s}`)}var pe=null,_c=null;async function Oe(){return pe||(_c||=(async()=>{try{pe=JSON.parse(await Eo.readFile(nm,"utf8"))}catch{pe={views:[],adoptWatermarks:{}}}finally{_c=null}return pe.views||=[],pe.adoptWatermarks||={},pe})(),_c)}async function he(){await Mt(nm,pe)}function $e(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 Ao=new Map,Cc=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,am=12e3;function Mc(t){return Ao.get(t)||null}function im(t,{name:e,box:n}={}){let r=Ao.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 Ao.set(t,o),o}function $c(t,e){let n=Ao.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function da(t,{error:e=null}={}){let n=Ao.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function cm(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 Oc(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function tT(t,e){let n=Oc(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!tm.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!tm.has(s))return s}}function _o(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=Bv.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 Nc(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 lm(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function eT(t,e){let n=new Set(t.views.map(o=>o.port)),r=lm();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=Zp;o<=Qp;o++)if(!(n.has(o)||r.has(o))&&!await _o(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Zp}-${Qp}`)}async function nT(t){try{let{stdout:e}=await me(N,P("list-sessions","-F","#{session_name}"));return e.split(`
77
+ `).some(n=>n.trim()===br(t))}catch{return!1}}async function Io(t){try{await me(N,P("kill-session","-t",br(t)))}catch{}}async function Pc(t){if(!await go())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=br(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 em(t,e=12){try{let{stdout:n}=await me(N,P("capture-pane","-t",br(t),"-p"));return String(n||"").trimEnd().split(`
78
78
  `).filter(Boolean).slice(-e).join(`
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!) {
79
+ `)}catch{return""}}var Re=new Map,rT=12e4;async function oT(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await nT(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await em(t.slug)||"server exited before listening"),Re.delete(t.id),await $e(t,t.state,t.error?{error:t.error}:{}),!0}if(await _o(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>rT?(t.state="error",t.error=await em(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 Ic=!1;async function fa(){if(!Ic){Ic=!0;try{let t=await Oe(),e=!1;for(let n of t.views)await oT(n)&&(e=!0);e&&await he()}finally{Ic=!1}}}async function pa({trackId:t}={}){await fa();let e=await Oe();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function um(t){let n=(await Oe()).views.find(r=>r.id===t);return n?{...n}:null}function Bc(t){return pe&&pe.views.find(e=>e.slug===t)||null}async function Dc({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 Zv(r,u),await Qv(u,n)},d=await Oe();if(i!=="detected"){let g=Oc(e),y=String(n).trim(),b=d.views.find(S=>S.trackId===t&&(S.slug===g||Oc(S.name)===g||S.cmd===y&&Bt.resolve(S.serveCwd??S.cwd)===u));if(b)return sT(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>=rn)throw new Error(`view cap reached (${rn} live) \u2014 stop one first`);let m={id:Dv().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:tT(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await eT(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Pc(m),d.views.push(m),await he(),await $e(m,"created",{origin:i}),$c(t,m.id),i==="detected"&&await _o(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await he(),await $e(m,"live")),{...m}}async function sT(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 Xv(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await _o(e.port))return await he(),$c(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>=rn)throw new Error(`view cap reached (${rn} live) \u2014 stop one first`);return e.origin==="detected"?(await Nc(e.port),e.origin="agent"):await Io(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 Pc(e),await he(),await $e(e,"created",{restarted:!0,reused:!0}),$c(e.trackId,e.id),{...e,reused:!0}}async function dm(t){let n=(await Oe()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await Nc(n.port):await Io(n.slug),n.state!=="stopped"&&(n.state="stopped",Re.delete(n.id),await he(),await $e(n,"stopped")),{...n}}async function fm(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>=rn)throw new Error(`view cap reached (${rn} live) \u2014 stop one first`);return await Io(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Re.delete(n.id),await Pc(n),await he(),await $e(n,"created",{restarted:!0}),{...n}}async function pm(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 Nc(r.port):await Io(r.slug),e.views.splice(n,1),Re.delete(r.id),await he(),await $e(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function mm(t){let e=await Oe(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Io(r.slug),Re.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await he()),n.length}var aT=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function hm(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 he()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(aT)){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(!lm().has(c)&&!n.views.some(l=>l.port===c)&&await _o(c))try{i.push(await Dc({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 iT from"node:os";import{randomUUID as cT}from"node:crypto";var Pn=process.env.BOSTRAT_STATE_DIR||Pe.join(process.env.HOME||iT.tmpdir(),".bostrat"),gm=Pe.join(Pn,"runbooks.json"),ma=Pe.join(Pn,"runbooks"),wm=256*1024,lT=[{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 Ro(t){return lT.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Ce=null,jc=null;async function Be(){return Ce||(jc||=(async()=>{let t=!0;try{Ce=JSON.parse(await Ne.readFile(gm,"utf8"))}catch{Ce={runbooks:[]},t=!1}finally{jc=null}return Ce.runbooks||=[],!t&&pt()&&await uT().catch(()=>{}),Ce})(),jc)}async function Bn(){await Mt(gm,Ce)}var $o=t=>Promise.resolve(t).catch(()=>{});function ha(t){return $o(Rt("runbooks",{...t},{trackId:t.trackId}))}async function uT(){let{records:t,source:e}=await Tt("runbooks");if(!(e!=="plane"||!t.length)){await Ne.mkdir(ma,{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 js("runbooks",r.id,o.v,"md");typeof s=="string"&&await Ne.writeFile(Me(r,o.v),s).catch(()=>{})}Ce.runbooks.push(r)}Ce.runbooks.length&&await Bn()}}function kr(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 Me(t,e){return Pe.join(ma,`${t.id}.v${e}.md`)}function ym(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 bm(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>wm)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${wm/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ro(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 km(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function wa({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 Lc(t){let n=(await Be()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function xm({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=bm(r),c=await Be(),l={id:cT().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(ma,{recursive:!0}),await Ne.writeFile(Me(l,1),r),l.versions.push({v:1,mdRel:Pe.relative(Pn,Me(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Bn(),await kr(l,"created",{version:1}),ha(l),$o(Je("runbooks",l.id,1,"md",Me(l,1))),{...l}}async function Sm(t,{md:e,note:n}={}){let o=(await Be()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");km(o,"update");let s=bm(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Ne.mkdir(ma,{recursive:!0}),await Ne.writeFile(Me(o,a),e),o.versions.push({v:a,mdRel:Pe.relative(Pn,Me(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Bn(),await kr(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),ha(o),$o(Je("runbooks",o.id,a,"md",Me(o,a))),{...o}}async function vm(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 km(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Bn(),await kr(r,e==="done"?"done":"reopened"),ha(r),{...r})}async function Tm(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(Pn,o.mdRel),{force:!0}).catch(()=>{});return await Bn(),r.trackDeletedAt||await kr(r,"deleted"),$o(Qt("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function Am(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 kr(r,"orphaned"),r.cwd=null,ha(r);return n.length&&await Bn(),n.length}async function Em(t){let e=await Be(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await kr(r,"deleted");for(let o of r.versions||[])await Ne.rm(Pe.join(Pn,o.mdRel),{force:!0}).catch(()=>{});$o(Qt("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Bn()),n.length}async function _m(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Tt("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Be();for(let r of n.runbooks){await Rt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Je("runbooks",r.id,o.v,"md",Pe.join(Pn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import Mo from"node:fs/promises";import Um from"node:path";import FT from"node:os";import{randomUUID as Hm}from"node:crypto";import dT from"node:fs/promises";import Im from"node:path";import fT from"node:os";var Rm=()=>Im.join(process.env.BOSTRAT_STATE_DIR||Im.join(process.env.HOME||fT.tmpdir(),".bostrat"),"integrations.json");async function De(){try{return JSON.parse(await dT.readFile(Rm(),"utf8"))||{}}catch{return{}}}var ga=["linked","prOpened","prMerged","create"],Fc=["prompt","auto","off"],pT={create:["prompt","off"]},$m=t=>pT[t]??Fc;function ya(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of ga){let s=$m(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 Dn(){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:ya(t),defaultTeam:n}:null}async function ba(){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:ya(t)}}async function Uc(){return ya(await De())}async function Om(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${ga.join(", ")}`);for(let[r,o]of e){if(!ga.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${ga.join(", ")}`);let s=$m(r);if(!s.includes(o))throw new Error(s.length<Fc.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Fc.join(", ")}`)}let n=await De();return n.ticketFlow={...ya(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Mt(Rm(),n,{mode:384}),n.ticketFlow}async function Oo(){let t=(await De()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function mT(){let t=(await De()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var hT={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function ka(){let e=(await De()).voiceChat||{},n=e.provider||"openai",r=hT[n];if(!r)return null;let o=n==="openai"?await mT():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function Cm(){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 jn(){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 Wc={};Od(Wc,{createComment:()=>TT,findIssueUrl:()=>bT,issueStates:()=>_T,mdToAdf:()=>Pm,moveIssue:()=>IT,parseIssueRef:()=>yT,resolveIssue:()=>kT,uploadImage:()=>AT,upsertCard:()=>xT,whoami:()=>Hc});var xr=t=>String(t.baseUrl||"").replace(/\/$/,""),Nm=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Ln(t,e,n,r){let o=await fetch(`${xr(t)}${n}`,{method:e,headers:{authorization:Nm(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 wT=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,gT=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function yT(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(wT);return r?r[1].toUpperCase():null}let n=e.match(gT);return n?n[1]:null}function bT(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 kT(t,e){let n;try{n=await Ln(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:`${xr(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function xT(t,e,{url:n,title:r,subtitle:o}){await Ln(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 ST=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function vT(t){let e=[],n=0;for(let r of t.matchAll(ST)){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 Pm(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(...vT(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 TT(t,e,n){let r=await Ln(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Pm(n)});return r?.id?`${xr(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function Hc(t){let e=await Ln(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(xr(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function AT(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(`${xr(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:Nm(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??`${xr(t)}/rest/api/3/attachment/content/${c.id}`}var ET={new:"unstarted",indeterminate:"started",done:"completed"},Mm=t=>ET[t?.key]??"started";async function _T(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([Ln(t,"GET",`/rest/api/3/issue/${n}?fields=status`),Ln(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Mm(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Mm(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function IT(t,e,n){await Ln(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var Vc={};Od(Vc,{createComment:()=>BT,createIssue:()=>Gc,createProject:()=>Kc,findIssueUrl:()=>MT,issueStates:()=>jT,moveIssue:()=>LT,parseIssueRef:()=>CT,resolveIssue:()=>NT,teams:()=>Sr,uploadImage:()=>DT,upsertCard:()=>PT,whoami:()=>qc});var RT=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function we(t,e,n={}){let r=await fetch(RT(),{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 $T=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,OT=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function CT(t){let e=String(t||"").trim(),n=e.match($T);if(n)return n[1].toUpperCase();let r=e.match(OT);return r?r[1]:null}function MT(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 NT(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 PT(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 BT(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 qc(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 DT(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 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:
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 jT(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 LT(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 Sr(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 Bm=255;async function Kc(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>Bm,a=s?`${o.slice(0,Bm-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 Gc(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 Wm={linear:{api:Vc,config:Dn},jira:{api:Wc,config:ba}};async function tl(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(Wm)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function Fn(t){let e=Wm[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function el(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var qm=()=>process.env.BOSTRAT_STATE_DIR||Um.join(process.env.HOME||FT.tmpdir(),".bostrat"),Yc=()=>Um.join(qm(),"tickets.json"),Km=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),xa=t=>`${Km()}/track/${t}`,on=null,zc=null,Jc=Promise.resolve();async function Po(){return on||(zc??=Mo.readFile(Yc(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(on=t,!e&&pt()){let{records:n,source:r}=await Tt("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;on[s]=i}Object.keys(on).length&&await Zc().catch(()=>{})}}return on}).finally(()=>zc=null),zc)}async function Zc(){let t=JSON.stringify({tickets:on},null,2),e=`${Yc()}.${process.pid}.tmp`;await Mo.mkdir(qm(),{recursive:!0}),await Mo.writeFile(e,t),await Mo.rename(e,Yc())}async function sn(t=null){if(Jc=Jc.then(Zc,Zc),await Jc.catch(()=>{}),!t)return;let e=on?.[t];e?Rt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):Qt("tickets",t).catch(()=>{})}async function ge(t){return(await Po())[t]??null}async function Sa(){return{...await Po()}}async function Gm(){return(await tl()).length>0}async function vr(t,e,{source:n="api"}={}){let r=await tl();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 Po())[t.id]=c,await sn(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 No(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 Jm(t.id,c,"linked"),c}async function nl(t){let e=await Po();return e[t]?(delete e[t],await sn(t),!0):!1}var Qc=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Vm(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 UT(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Qc(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${Qc(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function HT(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(Qc(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function No(t,e){let n=await Fn(e);if(!n)return;let r=await Vm(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:xa(t),title:`Bostrat Track: ${e.track.name}`,subtitle:HT(e,r)})}function va(t){let e=new URL(Km());return`${e.protocol}//${t}.${e.host}/`}var WT={linked:"linked",opened:"prOpened",merged:"prMerged"};function qT(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 zm(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await sn(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 Jm(t,e,n){let r=await Fn(e);if(!r)return;let o=r.cfg.ticketFlow?.[WT[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=el(s,a),c=qT(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 zm(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:Hm(),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 Xm(t){let e=await ge(t);if(!e)throw new Error("no ticket linked to this Track");let n=await Fn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=el(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await sn(t)),{key:e.key,url:e.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function rl(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await ge(t);if(!r)throw new Error("no ticket linked to this Track");let o=await Fn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=el(s,a),c=a.find(u=>u.id===e);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await sn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await zm(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Ym(t,e){let n=await ge(t);if(!n)throw new Error("no ticket linked to this Track");let r=await Fn(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 Zm(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 rl(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 Dm=50;async function Qm(){let t=await Dn();if(!t)throw new Error("no ticketing integration configured");return Sr(t)}function KT(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>Dm)throw new Error(`too many issues (${n.length}); cap is ${Dm} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function GT(t,e,n,r){let o=r??await Sr(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 th(t,e){let n=await Dn();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=KT(e),o=await Sr(n),s=await GT(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Hm();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 eh(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 Dn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Sr(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 Kc(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 Gc(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 vr(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function Co(t,e){let n=await Fn(t);n&&await n.api.createComment(n.cfg,t,e)}async function VT(t,e,n){let r=await Fn(t);if(!r)return null;let o=await nn(e);if(!o)return null;let s=Ie(o,n);if(!s)return null;let a=await Mo.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 jm=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Xc=new Map;function zT(t,e){if(jm()<=0)return No(t,e);if(Xc.has(t))return;let n=setTimeout(()=>{Xc.delete(t),ge(t).then(r=>r?No(t,r):null).catch(()=>{})},jm());n.unref?.(),Xc.set(t,n)}var Lm=new Map;async function Fm(t,e,n){let r=Lm.get(t)??0;Date.now()-r<600*1e3||(Lm.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 JT(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 tl())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 vr(s,o,{source:"chat"})}catch(a){await Fm(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 sn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await sn(t.trackId),await No(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 sn(t.trackId),await No(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Vm(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: ${UT(o)}.`:"")),await Jm(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await VT(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}**: ${va(r.slug)}
90
+ _(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await zT(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Fm(t.trackId,s?.cwd??null,o)}}function nh({resolveTrack:t}){Jt.on("event",e=>{JT(e,{resolveTrack:t}).catch(()=>{})})}async function rh(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Tt("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Po(),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 XT from"node:crypto";import Ta from"node:fs/promises";import oh from"node:path";import YT from"node:os";var sh=process.env.BOSTRAT_STATE_DIR||oh.join(process.env.HOME||YT.tmpdir(),".bostrat"),al=oh.join(sh,"pr-links.json"),H=(t,e)=>`${t}#${Number(e)}`,ah=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=XT.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},an=null,ol=null,sl=Promise.resolve();async function gt(){return an||(ol??=Ta.readFile(al,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(an=t,!e&&pt()){let{records:n,source:r}=await Tt("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;an[H(i.repo,i.number)]=i}Object.keys(an).length&&await il().catch(()=>{})}}return an}).finally(()=>ol=null),ol)}async function il(){let t=JSON.stringify({links:an},null,2),e=`${al}.${process.pid}.tmp`;await Ta.mkdir(sh,{recursive:!0}),await Ta.writeFile(e,t),await Ta.rename(e,al)}async function vt(t){sl=sl.then(il,il),await sl.catch(()=>{});let e=an?.[t];e&&Rt("prlinks",{id:ah(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var ZT=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=H(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:ZT.has(a),firstSeen:Date.now()},await vt(H(t,e)),{link:i[c],created:!0})}async function Bo(t,e){if(!t||e==null)return null;let n=(await gt())[H(t,e)];return!n||n.announced?null:(n.announced=!0,await vt(H(t,e)),n)}async function $t(t,e){return!t||e==null?null:(await gt())[H(t,e)]??null}async function cn(t,e,n){if(!t||e==null)return null;let r=(await gt())[H(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 vt(H(t,e)),r):null}async function ih(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await gt())[H(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await vt(H(t,e)),o):null}async function ch(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await gt())[H(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await vt(H(t,e)),o):null}async function Aa(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await gt())[H(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await vt(H(t,e)),r)}async function Ea(t,e){if(!t||e==null)return null;let n=(await gt())[H(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await vt(H(t,e)),n)}async function _a(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await gt())[H(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await vt(H(t,e)),r)}async function Ia(t,e){if(!t||e==null)return null;let n=(await gt())[H(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await vt(H(t,e)),n)}async function lh(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await gt())[H(t,e)];return r?(r.baseFoldAt=n,await vt(H(t,e)),r):null}async function uh(t,e){if(!t||e==null)return null;let n=(await gt())[H(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await vt(H(t,e)),n)}async function dh(t,e,n){if(!t||e==null||!n)return null;let r=(await gt())[H(t,e)];return r?(r.lastHeadSha=n,await vt(H(t,e)),r):null}async function Do(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await gt())[H(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await vt(H(t,e)),r):null}async function Tr(t,e,n=!0){if(!t||e==null)return null;let r=(await gt())[H(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await vt(H(t,e)),r):null}async function Ra(t,e,n=!0){if(!t||e==null)return null;let r=(await gt())[H(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await vt(H(t,e)),r):null}async function fh(t,e,n){if(!t||e==null)return null;let r=(await gt())[H(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 vt(H(t,e)),r}async function jo(t,e){if(!t||e==null)return null;let n=(await gt())[H(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 vt(H(t,e)),n)}var QT=new Set(["merged","closed","lost"]);async function ph(){return Object.values(await gt()).filter(t=>!QT.has(t.state))}async function ye(){return Object.values(await gt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function mh(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Tt("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:ah(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var wh=t=>Math.round(t*100)/100;function tA(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():hh.basename(e.base)}))}function gh(t){let e=new Map;for(let n of t||[])for(let r of tA(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 cl=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function eA(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 ll(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(cl(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(cl(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(`${cl(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)?eA(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function nA(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 yh(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:wh(l),firstActivityAt:c,lastActivityAt:i}}async function bh(t){let e=gh(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=>yh(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function kh(t,e){let r=gh(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([ye().catch(()=>[]),Nn().catch(()=>[]),pa().catch(()=>[]),wa().catch(()=>[]),Sa().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:nA(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||hh.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:wh(m.totalUsd),lastActivityAt:$a(d)})}return{...yh(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as rA}from"node:child_process";import{promisify as oA}from"node:util";import{randomUUID as sA}from"node:crypto";var Ar=oA(rA),Oa=t=>new Promise(e=>setTimeout(e,t)),aA=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,xh=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,iA=250,cA=400,Sh={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}};async function ul(t,e){let n=Date.now()+e;for(;;){if(await ur(t))return!0;if(Date.now()>n)return!1;await Oa(iA)}}async function lA(t,e){let n=Sh[e]||Sh.default;n.escape&&(await bt(t,["Escape"],{kind:"swap-exit"}),await Oa(150)),n.wipe&&await qs(t,400,{kind:"swap-exit"}),await Ks(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=_e(e).submitDelayMs||0;await Oa(Math.max(r,250)),await bt(t,["Enter"],{kind:"swap-exit"})}async function uA(t,e){try{let{stdout:n}=await Ar(N,P("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function vh(t){await Ar(N,P("respawn-pane","-k","-t",t))}async function dl(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||Xe(n),a=Number(e.exitTimeoutMs)||aA;await U({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await ur(t)&&(await lA(t,o),!await ul(t,a)&&(i=!0,await U({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await vh(t),!await ul(t,xh))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&In(n),l=c?Rs():null,u=await uA(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Ar(N,P("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Ar(N,P("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?Fs(y,l):y}if(f&&!i&&(await vh(t),!await ul(t,xh)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||sA(),m=d?n:oc(n,p),g=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Ge(r.cwd),s==="codex"&&await rr(r.cwd),s==="cursor"&&await or(r.cwd),await On(t),await Ar(N,P("set-option","-t",t,"@bostrat-sid",p)),await Ar(N,P("set-option","-t",t,"@bostrat-agent",s)),await Oa(cA),await St(t,m,{kind:"swap-launch"}),await U({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 WA from"node:fs/promises";function dA(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 Lo(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 Fo(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 _r=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Et=t=>t&&!/[$`]/.test(t)?t:null,mt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Er=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},Eh=t=>t.startsWith("-"),Th=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(Eh(o)){e.has(o)&&r++;continue}n.push(o)}return n}var fA={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"},pA={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 _h(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 mA(t){let e=_h(t),n=Et(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 fl(t){if(t.includes("--files")){let s=Et(at(t,Th)[0]);return s&&s!=="."?`List files in ${Er(s)}`:"List files"}let e=at(t,Th),n=e[0];if(!n)return null;let r=Et(e.slice(1).find(s=>s!==".")),o=_r(n,40);return r?`Search for \u201C${o}\u201D in ${Er(r)}`:`Search for \u201C${o}\u201D`}function pl(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 ml(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 hA(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 Ah(t){return t.find(e=>/^https?:\/\//.test(e))||null}var wA={git:t=>{let e=at(t,new Set(["-C","-c"])),n=fA[e[0]];return n?n(e,t):null},gh:t=>{let e=at(t),n=pA[e[0]];return n?n(e):null},sed:mA,rg:fl,grep:fl,egrep:fl,nl:t=>{let e=Et(at(t)[0]);return e?`Read ${mt(e)}`:null},cat:t=>{let e=Et(at(t)[0]);return e?`Read ${mt(e)}`:null},head:t=>{let e=Et(at(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${mt(e)}`:null},tail:t=>{let e=Et(at(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${mt(e)}`:null},wc:t=>{let e=Et(at(t)[0]);return e?`Count lines in ${mt(e)}`:"Count input lines"},ls:t=>{let e=Et(at(t)[0]);return e&&e!=="."?`List files in ${Er(e)}`:"List files"},find:t=>Et(t[1])&&!Eh(t[1])&&t[1]!=="."?`Find files in ${Er(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Et(at(t)[0]);return e?`Create directory ${Er(e)}`:null},touch:t=>{let e=Et(at(t)[0]);return e?`Create ${mt(e)}`:null},rm:t=>{let e=Et(at(t)[0]);return e?`Delete ${Er(e)}`:null},mv:t=>{let e=Et(at(t)[0]);return e?`Move ${mt(e)}`:null},cp:t=>{let e=Et(at(t)[0]);return e?`Copy ${mt(e)}`:null},chmod:t=>{let e=Et(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=Ah(t);return e?`Fetch ${_r(e,56)}`:"Fetch a URL"},wget:t=>{let e=Ah(t);return e?`Fetch ${_r(e,56)}`:"Fetch a URL"},npm:pl,pnpm:pl,yarn:pl,npx:t=>{let e=at(t)[0];return e?`Run ${e}`:null},node:t=>ml(t,"Node"),python:t=>ml(t,"Python"),python3:t=>ml(t,"Python"),make:t=>{let e=at(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:hA,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}},gA=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function yA(t){let e=Fo(Lo(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(mt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return hl(e[2]);let n=mt(e[0]);if(gA.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=wA[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function hl(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=dA(e);for(let i=0;i<r.length-1;i++){let c=Fo(Lo(r[i])),l=Fo(Lo(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(mt(c[0]))&&mt(l[0])==="sed"){let u=_h(l),f=at(c)[0];if(u&&f)return _r(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=yA(i);c&&o.push({d:c,head:mt(Fo(Lo(i))[0]||""),sub:at(Fo(Lo(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":_r(s?s.d:o[0].d,88)}var gl=new Set(["Bash"]);function Ir(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 ln(t,e=""){let n=e.trim();return n||hl(t)||Ir(t)}function wl(t,e){if(t){if(Array.isArray(t)){for(let n of t)wl(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&gl.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))wl(n,e)}}}async function Ih(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(wl(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:ln(f.command,f.description),ts:u})}return o.slice(-n)}function Rh(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 bA=/^Async (?:agent|task) launched/i,kA=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 $h(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?Ir(p):null),detail:p?Ir(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&bA.test(kA(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 Uo from"node:fs";import SA from"node:fs/promises";import Un from"node:path";import vA from"node:os";import TA from"node:crypto";import AA from"node:zlib";var Ca=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,Rr=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function un(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 yl=t=>`"${String(t).replace(/"/g,'\\"')}"`;function xA(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 Oh(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 ${un(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Rr(r)}`:`Read ${Rr(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?un(e.path,n):null,s=e?.glob?` --glob ${yl(e.glob)}`:"",a=ne(String(r),40);return{command:ne(`grep ${yl(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?un(e.target_directory,n):null;return{command:ne(`glob ${yl(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${ne(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:ne(`edit ${un(e.path,n)}`,140),description:`Edit ${Rr(e.path)}`}:null;case"Write":return e?.path?{command:ne(`write ${un(e.path,n)}`,140),description:`Write ${Rr(e.path)}`}:null;case"Delete":return e?.path?{command:ne(`delete ${un(e.path,n)}`,140),description:`Delete ${Rr(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>un(s,n)).filter(Boolean);return{command:ne(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=xA(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:ne(`apply_patch ${un(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Rr(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 EA=process.env.BOSTRAT_CURSOR_CHATS||Un.join(vA.homedir(),".cursor","chats");function Nh(t){let e=TA.createHash("md5").update(String(t)).digest("hex");return Un.join(EA,e)}function Ph(t){try{let e=JSON.parse(Uo.readFileSync(Un.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 _A(t){let e=Nh(t),n;try{n=Uo.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Un.join(e,o.name),a=Un.join(s,"store.db");if(!Uo.existsSync(a))continue;let i=Ph(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=Un.join(Nh(t),e),r=Un.join(n,"store.db");if(Uo.existsSync(r))return{id:e,dir:n,store:r,...Ph(n)||{createdAtMs:null,updatedAtMs:null}}}return _A(t)}function Ch(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function IA(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=AA.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function RA(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 $A(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!Ca.has(r.toolName))continue;let o=Oh(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 OA=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():"",CA=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],Mh=process.getBuiltinModule?.("node:sqlite"),$r={turns:[],trailingCommands:[],model:null,requestIds:[]};async function bl(t,e){if(!Mh)return $r;let{DatabaseSync:n}=Mh,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?Ch(r,s.latestRootBlobId):null;if(!i)return{...$r,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of RA(i)){let m=IA(Ch(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(...$A(m.content,e).map((S,E)=>({...S,id:S.id||`${p}:${E}`}))),u.push(...CA(m.content));let y=OA(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 Bh(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(Uo.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 Dh(t,e,{limit:n=80}={}){let r=Ma(t,e);if(!r)return[];let{turns:o,model:s}=await bl(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 jh(t,e){let n=Ma(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await bl(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 Lh(t,e,{limit:n=80}={}){let r=Ma(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await bl(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:ln(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:ln(u.command,u.description),ts:i+1});return l}async function Fh(t,e){let n=Ma(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await SA.stat(n.store)).mtimeMs)}catch{return null}}import Uh from"node:fs";import xl from"node:fs/promises";import kl from"node:path";import MA from"node:os";var NA=process.env.BOSTRAT_CODEX_SESSIONS||kl.join(process.env.CODEX_HOME||kl.join(MA.homedir(),".codex"),"sessions");async function Hh(t){try{return await xl.realpath(t)}catch{return t}}function PA(t=NA){let e=[],n=r=>{let o;try{o=Uh.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=kl.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:Uh.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function BA(t,e=1048576){let n;try{n=await xl.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 DA(t){let e=await BA(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 Hn(t,e){if(!t&&!e)return null;let n=PA();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await Hh(t);for(let o of n){let s=await DA(o.file);if(s&&(s===t||await Hh(s)===r))return o}return null}var jA=new Set(["output_text","text"]),LA=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&jA.has(e.type)).map(e=>e.text).join(`
98
+ `).trim():"";async function Wh(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Hn(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:vl,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,Sl.has(g.name)||c.push(g.name);continue}if(g.type!=="message"||g.role!=="assistant")continue;u=!0;let y=LA(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 Ho=new Set(["exec_command","shell","exec"]),Sl=new Set(["wait","write_stdin"]),vl="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 FA(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 HA(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 qh(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Hn(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"&&Ho.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=FA(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=HA(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:ln(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Kh(t,e){let n=await Hn(t,e);if(!n)return null;try{return Math.round((await xl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var Or=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}},Tl=class extends Or{transcript(e){return!e?.cwd||!e?.sessionId?null:Ae(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?pc(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?Ih(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?$h(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await WA.stat(n)).mtimeMs)}catch{return null}}},Al=class extends Or{async readMessages(e,n){return Dh(e.cwd,e.sessionId,n)}async readCommands(e,n){return Lh(e.cwd,e.sessionId,n)}async updatedAt(e){return Fh(e.cwd,e.sessionId)}},El=class extends Or{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return Wh(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return qh(e.cwd,this.sessionId(e),n)}async updatedAt(e){return Kh(e.cwd,this.sessionId(e))}},qA=new Or("unknown"),Gh=new Map;function _l(t){return Gh.set(t.agent,t),t}function Wo(t){return Gh.get(t)||qA}_l(new Tl("claude",gl));_l(new Al("cursor",Ca));_l(new El("codex",Ho));var Il=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function Rl(t,e={}){return(await Wo(t.agent).readMessages(t,e)).map(Il(t))}async function Vh(t,e={}){return(await Wo(t.agent).readCommands(t,e)).map(Il(t))}async function zh(t,e={}){return(await Wo(t.agent).readJobs(t,e)).map(Il(t))}var Yh=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Cl=/Chat about this/i,Wn=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Jh=/^(type something|chat about this|submit)\b/i,qn=/^[─—-]{6,}\s*$/,Ml=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function Kn(t){let e=t.search(Ml);return(e>=0?t.slice(0,e):t).trim()}function KA(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function GA(t){let e=String(t).trim();return!!(!e||qn.test(t)||Ml.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function VA(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=zA(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function zA(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 Xh(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Zh=/^\s*Switch model\?\s*$/m;function JA(t,e=""){let n=String(t);if(!Zh.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(Wn);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!qn.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:Kn(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 Qh=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,XA=/^\s*[─—▔-]{6,}\s*$/;function YA(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||!Qh.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(Wn);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&&!(XA.test(n[c])||qn.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Kn(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 ZA(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(Wn);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 $l=/Resume from summary/i,QA=/^\s*This session is\b/;function tE(t,e=""){let n=String(t);if(!$l.test(n))return null;let r=n.replace(/\r/g,"").split(`
105
+ `),o=-1;for(let u=r.length-1;u>=0;u--)if(QA.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(Wn);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!qn.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>$l.test(u.label)))return null;let i=s.map(u=>({label:Kn(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 Gn=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function Nl(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(Wn);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 tw=/Choose the text style that looks best with your terminal/,ew=/Let's get started\./;function eE(t,e=""){let n=String(t);if(!tw.test(n)||!ew.test(n)||Gn.test(n))return null;let r=n.replace(/\r/g,"").split(`
106
+ `),o=Nl(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:Kn(s.label),description:""}))}]}:null}var nw=/Select login method:/;function nE(t,e=""){let n=String(t);if(!nw.test(n)||Gn.test(n))return null;let r=n.replace(/\r/g,"").split(`
107
+ `),o=Nl(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Kn(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 Pl=/Paste code here if prompted/,rE=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function oE(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!rE.test(o)||Pl.test(t[r]))break;n+=o}return n}function sE(t,e=""){let n=String(t);if(!Pl.test(n)||Gn.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=oE(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 Ol=/OAuth error:/,aE=/Press Enter to retry\./;function iE(t,e=""){let n=String(t);if(!Ol.test(n)||!aE.test(n)||Gn.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
109
+ `)].reverse().find(a=>Ol.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 rw=/Quick safety check: Is this a project you created or one you trust/;function cE(t,e=""){let n=String(t);if(!rw.test(n)||Gn.test(n))return null;let r=n.replace(/\r/g,"").split(`
110
+ `),o=Nl(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:Kn(s.label),description:""}))}]}:null}var ow=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function lE(t,e=""){let n=String(t);if(Gn.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(ow);if(!s)return null;let a=[];for(let l=o-1;l>=0&&a.length<3;l--){let u=r[l].trim();if(!u){if(a.length)break;continue}if(qn.test(r[l])||Ml.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 uE(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Cl.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 dE(t,e=""){let n=String(t);if(!Yh.test(n)||!Cl.test(n))return null;let r=n.replace(/\r/g,"").split(`
112
+ `),o=uE(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(Wn);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)&&!(Wn.test(r[C])||qn.test(r[C]));C++){let q=r[C].trim();if(!(!q||GA(r[C])||Jh.test(q))){v.desc=KA(q);break}}let f=l.filter(v=>!Jh.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&&!qn.test(r[v])&&!/[☐☒]/.test(r[v])&&g.push(C)}let y=g.join(" ")||(s>=0?Xh(r[s]):"")||"The agent is asking a question",b=s>=0?VA(r[s],y):null,S=b?.currentTabLabel||(s>=0?Xh(r[s]):""),E=f.map(v=>({label:Kn(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 Cr(t,e){let n=String(e||"");return n?t==="claude"?Yh.test(n)&&Cl.test(n)||Zh.test(n)||$l.test(n)||Qh.test(n.trimEnd().split(`
113
+ `).pop()||"")?!0:Gn.test(n)?!1:tw.test(n)&&ew.test(n)||nw.test(n)||Pl.test(n)||Ol.test(n)||rw.test(n)||ow.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 Vn(t,e){return!t||!e?null:t.agent==="claude"?ZA(e,t.name)||dE(e,t.name)||JA(e,t.name)||tE(e,t.name)||iE(e,t.name)||sE(e,t.name)||nE(e,t.name)||eE(e,t.name)||cE(e,t.name)||lE(e,t.name)||YA(e,t.name):t.agent==="cursor"||t.agent==="codex"?Vf(t.agent,e,t.name):null}async function Bl(t){if(!t)return null;let e=await Ze(t.name).catch(()=>"");return Vn(t,e)}function sw(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 aw(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 Mr(t){return typeof t=="string"?t.trim():""}var iw=/^\s*─{8,}\s*$/,fE=/^❯[  ]?(.*)$/;function qo(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(iw.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(iw.test(n[c])){o=c;break}if(o<0)return null;let s=n.slice(o+1,r);if(!s.length)return null;let a=s[0].match(fE);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 cw(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 lw(t,e,n,{busy:r=!1}={}){let o=qo(t,n);return o&&Dl(e,o)?"stuck":r||o?"submitted":"unknown"}function Dl(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 pE from"node:crypto";var jl=new Set(["cursor","codex"]),dw=4,mE=4,hE=2,wE=24,uw=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Ko=t=>typeof t=="string"?t.trim():"";function gE(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,dw)){let o=Ko(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,mE)){let c=Ko(i?.label);if(!c)return null;let l=Ko(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<hE)return null;n.push({question:o,header:Ko(r?.header).slice(0,wE),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Ll(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;uw.lastIndex=0;let e=null;for(let o;o=uw.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=gE(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function yE(t,e,n){let r=pE.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Fl(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=dw}={}){if(!Array.isArray(t)||!e||!jl.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||Ll(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=yE(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 fw(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=Ko(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 iw=`
118
+ ${o}`),s||""}var pw=`
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,44 @@ 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 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(`
138
+ `;import{execFile as bE}from"node:child_process";import{promisify as kE}from"node:util";var Hl=kE(bE),mw=64*1024*1024;async function ht(t,e,{input:n}={}){let r=Hl("git",["-C",t,...e],{maxBuffer:mw});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Na(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 hw(t){let{stdout:e}=await ht(t,["rev-parse","--show-toplevel"]);return e.trim()}async function xE(t){try{return await ht(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Go(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)=>Hl("git",["-C",t,...a],{maxBuffer:mw,env:o,...i});try{let a=await xE(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 Hl("rm",["-f",r])}catch{}}}async function Wl(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 ql(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 Kl(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 Nr(t,e,n,r){return await ht(t,["worktree","add","--no-track","-b",n,e,r]),e}async function ww(t,e){try{return await ht(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function gw(t,e,n){return await ht(t,["worktree","add",e,n]),e}async function Pa(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 yw(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 Xt(t,e){try{await ht(t,["worktree","remove","--force",e])}catch{try{await ht(t,["worktree","prune"])}catch{}}}function SE(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 Gl(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Vl(t,e,n,r){let{stdout:o}=await ht(t,Gl(["diff","--shortstat",e,n],r));return SE(o)}async function zl(t,e,n,r){let{stdout:o}=await ht(t,Gl(["diff","--name-only",e,n],r));return o.split(`
140
+ `).map(s=>s.trim()).filter(Boolean)}async function Jl(t,e,n,r){let{stdout:o}=await ht(t,Gl(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Ul(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Ul(t,o,["--3way","--whitespace=nowarn"]),a=await vE(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Ul(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Ul(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 vE(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 TE from"node:fs/promises";import AE from"node:os";import bw from"node:path";var Ba=t=>new Promise(e=>setTimeout(e,t)),EE=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,_E=300,vw=[{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"}],IE=new Set([...vw.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),RE=[{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"}],Tw=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Aw=[{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 Ew(t){return t==="codex"?RE:t==="cursor"?Aw:vw}function kw(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 xw(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 $E(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 _w(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&&!Tw.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=Aw.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!IE.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Vo(t,e,{timeoutMs:n=EE}={}){let r=Date.now()+n;for(;;){let o=await ft(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await Ba(_E)}}async function OE(t,e){return await St(t,`/model ${e.model}`,_e("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function CE(t,e){await Ws(t),await St(t,"/model",{enter:!1}),await Ba(250),await bt(t,["Enter"],{kind:"model-switch"});let n=await Vo(t,kw);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=$E(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await St(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await Vo(t,i=>xw(i)||!kw(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?Tw.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 St(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Vo(t,i=>!xw(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 ME(){let t=bw.join(process.env.CURSOR_CONFIG_DIR||bw.join(AE.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await TE.readFile(t,"utf8"))?.model||null}catch{return null}}async function NE(t,e){await Ws(t),await Ks(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 bt(t,["Down"],{kind:"model-switch"}),await Ba(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 Ba(800);let a=await ME();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 Iw(t,e,n){return t==="codex"?CE(e,n):t==="cursor"?NE(e,n):OE(e,n)}var Kt=t=>t||null;function ja(t){let e=t||{},n=PE(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function PE(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:{...Pr(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",...Pr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{change:"subtask-resumed",...Pr(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",...Pr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Pr(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",...Pr(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 Da=Promise.resolve(),Yl=t=>(Da=Da.then(t).catch(()=>{}),Da),$w=()=>Da;function Ow(t,e){let n=ja(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Yl(()=>R(r,n))}var Rw=new Set;async function Cw(t){if(!(!t?.id||Rw.has(t.id))){Rw.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=ja(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function Pr(t){let{type:e,at:n,...r}=t;return r}var Xl=new Map;function Mw(t,e,n){!n?.promptId||Xl.get(t.id)===n.promptId||(Xl.set(t.id,n.promptId),Yl(()=>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={}){Xl.delete(t.id),Yl(()=>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 p_}from"node:crypto";var Pw=/(?<!\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,Dw=/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,jw=/(?<!\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,BE=new RegExp(`\\b(?:${Pw}|${Bw}|${Dw}|${jw})\\b`,"i"),DE=/\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,jE=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,LE=90;function FE(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function Lw(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of FE(n).reverse()){if(!BE.test(r))continue;let o=r.endsWith("?")&&r.length<=LE&&jE.test(r);if(DE.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var Fw=["commit","rebase","push","pr"],UE=new RegExp(`\\b(?:${Pw})\\b`,"i"),HE=new RegExp(`\\b(?:${jw})\\b`,"i"),WE=new RegExp(`\\b(?:${Bw})\\b`,"i"),qE=new RegExp(`\\b(?:${Dw})\\b`,"i"),KE=/\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 Uw(t){let e=String(t||""),n=[];UE.test(e)&&n.push("commit"),HE.test(e)&&n.push("rebase"),WE.test(e)&&n.push("push");let r=KE.test(e);!r&&qE.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 La(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 GE=3e4;function Hw(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<=GE&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Ww(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 Nw={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function qw(t){let e=(Array.isArray(t)?t:[]).filter(r=>Nw[r]).map(r=>Nw[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 YE}from"node:child_process";import{promisify as ZE}from"node:util";import VE from"node:fs";import Ql from"node:fs/promises";import zw from"node:path";import zE from"node:os";var Jw=process.env.BOSTRAT_STATE_DIR||zw.join(process.env.HOME||zE.tmpdir(),".bostrat"),Fa=zw.join(Jw,"github-metrics.json"),Xw=60*6e4,Kw=5*6e4,JE=6e4,Br=Date.now(),je=new Map,dn=null,zn=0,Gw=0,Ua=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(VE.readFileSync(Fa,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Xw?(Br=t.windowStart,je=new Map(Object.entries(t.byCaller||{})),dn=t.lastHour||null):Ua(new Map(Object.entries(t.byCaller||{})))&&(dn={start:t.windowStart,total:Ua(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var Vw=Promise.resolve();function Yw(t,e=!1){if(!e&&t-Gw<JE)return;Gw=t;let n=JSON.stringify({windowStart:Br,byCaller:Object.fromEntries(je),lastHour:dn});Vw=Vw.then(()=>Ql.mkdir(Jw,{recursive:!0})).then(()=>Ql.writeFile(`${Fa}.tmp`,n)).then(()=>Ql.rename(`${Fa}.tmp`,Fa)).catch(()=>{})}function Zw(t){t-Br<Xw||(dn={start:Br,total:Ua(je),byCaller:Object.fromEntries(je)},dn.total&&console.log(`[github] ${dn.total} calls last hour`,dn.byCaller),Br=t,je=new Map,Yw(t,!0))}function Le(t="other",e=Date.now()){Zw(e),je.set(t,(je.get(t)||0)+1),Yw(e)}function Qw(t=Date.now()){return Zw(t),{windowStart:Br,total:Ua(je),byCaller:Object.fromEntries(je),lastHour:dn,backoffUntil:zn>t?zn:null}}var XE=/rate limit|too quickly|abuse detection|HTTP 429/i;function fn(t){return XE.test(String(t?.message||t||""))}function pn(t,e=Date.now()){return zn<=e&&console.warn(`[github] rate limited \u2014 backing off ${Kw/6e4} min:`,String(t?.message||t||"").slice(0,200)),zn=Math.max(zn,e+Kw),zn}function Ha(t=Date.now()){return Math.max(0,zn-t)}async function tu(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await ft(t,{lines:xt}).catch(()=>"");if(!Ee(e,r))return Yf(e,r)?{working:!0,prompted:!1}:n;if(Cr(e,r)){let o=await Ze(t).catch(()=>r);if(Vn({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function Wa(t,e){return(await tu(t,e)).working}var tg=ZE(YE),eg=[4e3,15e3,45e3],QE=2500,t_=5*6e4;async function eu(t,e,{midTurn:n=Wa,pollMs:r=QE,capMs:o=t_}={}){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 e_=6e4,n_=9e4,r_=2e3,o_=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,s_=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,a_=/\s--web\b/,ng=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function nu(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function rg(t){let e=String(t||""),n=e.match(o_);if(!n)return null;let r=nu(e,n.index);return a_.test(r)?null:{repo:r.match(ng)?.[1]??null}}var i_=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function og(t){let e=String(t||""),n=e.match(s_);if(!n)return null;let r=nu(e,n.index),o=nu(e,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){i_.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(ng)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var c_="number,url,title,headRefName,createdAt";async function l_(t,e){let n=async a=>{Le("observer");let{stdout:i}=await tg("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",c_],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...a}}).catch(c=>{throw fn(c)&&pn(c),c});return JSON.parse(i||"[]")},r=a=>(a||[]).filter(i=>Date.parse(i.createdAt)>=e-e_),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 u_(t,e){if(e)return[e];let n=[];for(let r of sg(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 sg(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 d_(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await u_(t,e)){let a;try{a=await l_(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 mn=new Set;function ag(t,e,{delays:n=eg,turnEnd:r}={}){let o=rg(e.command);if(!o)return null;let s=!!og(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(mn.has(a))return null;mn.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 d_(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 Bo(f,d.number))?null:(await eu(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{mn.delete(a)}})();return c.catch(()=>mn.delete(a)),c}var f_="number,url,title,headRefName,state";function ig(t,e,{delays:n=eg,turnEnd:r,openedWait:o}={}){let s=og(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(mn.has(a))return null;mn.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=sg(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 Do(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 eu(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{Le("observer");let{stdout:j}=await tg("gh",["pr","view",String(l),"--repo",$,"--json",f_],{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){fn(j)&&pn(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 eu(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),J=0;if(!M(E)){if(rg(e.command)&&await Bo(c,b.number)){let $=Date.now();J=$+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??n_,j=o?.pollMs??r_,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(),J),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await wn(c,b.number).catch(()=>{}),await hn(c,b.number).catch(()=>{}),await cn(c,b.number,"merged"),b}return p?d(p,!1):null}finally{mn.delete(a)}})();return i.catch(()=>mn.delete(a)),i}function ou(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function cg(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function lg(t){return`job|${t.box}|id:${t.id}`}var ru=new Map,qa=new Map;async function su(t){return ru.has(t.id)?ru.get(t.id):(qa.has(t.id)||qa.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(ou({role:o,sourceId:r.payload.sourceId})),n.keys.add(ou({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(cg({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(lg({id:r.payload.id,box:r.boxId}));return ru.set(t.id,n),n}).finally(()=>qa.delete(t.id))),qa.get(t.id))}async function ug(t,e){try{let n=(await su(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=ou(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=Lw(r.text);if(s){let{offered:a,toPr:i}=Uw(s),c;if(t.workspace?.kind==="multi"){let l=await zo(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:p_(),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 dg(t,e){try{let n=await su(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=cg(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}}),ag(t,o),ig(t,o))}}catch{}}async function fg(t,e){try{let n=await su(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=lg(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 m_=/^Base directory for this skill:\s*(\S+)/,h_=/^Run the "([^"]+)" workflow\./,w_=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,g_=/^<local-command-(?:stdout|caveat)>/i,y_=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,b_=/^Continue from where you left off\.$/,k_=/^<task-notification>/i,x_=/<summary>([\s\S]*?)<\/summary>/i,S_=/<task-id>([\s\S]*?)<\/task-id>/i,v_=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,T_=/<status>([\s\S]*?)<\/status>/i,A_=/<result>([\s\S]*?)(?:<\/result>|$)/i;var au=new Set(["model","clear","compact","login","exit","quit"]);function pg(t){return au.has(String(t||"").replace(/^\//,"").toLowerCase())}var E_=/^\/([a-z0-9:_-]+)$/i;function Ka(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(m_);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(h_);if(r)return{kind:"skill",name:r[1]};let o=e.match(w_);if(o){let a=o[1].toLowerCase();return au.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(E_);if(s&&au.has(s[1].toLowerCase()))return{kind:"suppress"};if(g_.test(e))return{kind:"suppress"};if(b_.test(e))return{kind:"suppress"};if(y_.test(e))return{kind:"suppress"};if(k_.test(e)){let a=c=>__(e.match(c)?.[1].trim()||"")||null,i=a(A_);return{kind:"task",summary:a(x_)||"",taskId:a(S_),toolUseId:a(v_),status:a(T_)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function __(t){return t.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&amp;/g,"&")}function mg(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 hg(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var it=t=>t.payload?.ts??t.ts,I_=new Set(["opened","ci-fixed","reconciled","pushed"]),wg=t=>`${t.repo??""}#${t.number}`;function R_(t,e,n){let r=wg(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 Jo(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:it(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,J=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()},q=6e4,rt=new Map,st=(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||Ka(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"&&pg(T.command))break;T.kind!=="suppress"&&e.push({id:w.id,kind:"event",type:hg(T),name:T.name??null,command:T.command??null,summary:T.summary??null,ts:it(w),box:w.boxId,agent:w.provider});break}}if(r.has(`${w.boxId}|${it(w)}`)||w.source==="transcript"&&st(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:it(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:it(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,it(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=>!Sl.has(A)&&!Ho.has(A)):h.tools||[],O=h.text,I=null;if(jl.has(w.provider)){let A=Ll(String(h.text||""));A&&(O=A.text,I={questions:A.questions})}e.push({id:w.id,kind:"message",role:"assistant",text:O,ts:it(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=it(w),_=i.get(h.id);if(!_&&(h.kind||"agent")==="agent"&&h.name){let K=l.get(h.name);K&&K.ts>=T&&(_=K)}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=rt.get(w.boxId),A=I?.jobs[I.jobs.length-1]?.startedTs;if(I&&A!=null&&T-A<=q)I.jobs.push(O);else{let K={id:w.id,kind:"job-dock",dockId:h.id,ts:T,box:w.boxId,agent:w.provider,jobs:[O]};rt.set(w.boxId,K),e.push(K)}break}case"handoff.done":e.push(gn(w,"lead-handoff"));break;case"subtask.delegated":e.push({...gn(w,"subtask-delegated"),to:w.boxId,toAgent:w.provider});break;case"subtask.merged":e.push(gn(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=gn(w,`pr-${h.change}`);if(I_.has(h.change)&&h.number!=null){let _=R_(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 _=wg(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:it(w),ts:it(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:it(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:it(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 _=gn(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:it(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:it(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:it(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:it(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:it(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:it(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:it(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:it(w),box:w.boxId,agent:w.provider};M.set(h.offerId,T),J.set(h.offerId,T),e.push(T);break}case"ticket.move.answered":{let T=J.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(gn(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:it(w),box:w.boxId,agent:w.provider});break}case"ticket.flow.changed":e.push(gn(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:it(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:it(w),box:w.boxId,agent:w.provider});break}case"error":{if(!h.kind)break;e.push(gn(w,h.kind));break}case"provider.connected":{e.push({id:w.id,kind:"event",type:"provider-connected",provider:h.provider||w.provider,ts:it(w),at:it(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:it(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(K=>K.index===O&&qt(K.question)===qt(I.question));A>=0?_.questions[A]=I:_.questions.push(I),_.questions.sort((K,dt)=>K.index-dt.index),_.questionTotal=Math.max(_.questionTotal,h.questionTotal??1),_.questions.length>=_.questionTotal&&(e.push(_),v.delete(T));break}default:break}}return O_(e.sort((w,h)=>(w.ts??0)-(h.ts??0)))}var $_=2*6e4;function O_(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<=$_){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 iu(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:C_(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??Jo(t)).filter(o=>o.kind==="message");return Rh(n,r)}function C_(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Ir(t.command)?e:ln(t.command,t.description||"")}function gn(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 M_(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 cu=new Map;async function N_(t,e){let n=`${t.id}:${e.name}`;if(!cu.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);cu.set(n,o)}return cu.get(n)}async function gg(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Hn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await N_(t,e),o=M_(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 Ga from"node:fs";import uu from"node:path";import P_ from"node:os";var B_=process.env.BOSTRAT_STATE_DIR||uu.join(process.env.HOME||P_.tmpdir(),".bostrat"),bg=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,D_="https://cursor.com/api/dashboard/get-filtered-usage-events",yg=100;async function j_({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(D_,{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:yg})});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<yg)break}return r}function L_(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}var F_=5*6e4,yn={at:0,rows:null,error:null},Va=()=>uu.join(B_,"cursor-usage.json");function kg(){try{return JSON.parse(Ga.readFileSync(Va(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function U_(t){try{Ga.mkdirSync(uu.dirname(Va()),{recursive:!0});let e=Va()+".tmp";Ga.writeFileSync(e,JSON.stringify(t)),Ga.renameSync(e,Va())}catch{}}var H_=5e3;async function W_({fetchImpl:t,now:e=Date.now()}={}){if(yn.rows&&e-yn.at<F_)return yn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=kg(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await j_({sinceMs:o,cookie:n,fetchImpl:t});yn={at:e,rows:s.map(L_).filter(Boolean),error:null}}catch(s){yn={at:e,rows:null,error:String(s?.message||s)}}return yn.rows}var du=()=>({enabled:bg(),lastPollAt:yn.at||null,error:yn.error}),lu=new Map;async function q_(t,e){let n=`${t.id}:${e.name}`;if(!lu.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);lu.set(n,o)}return lu.get(n)}async function xg(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!bg())return;let r=await W_({fetchImpl:n});if(!r||!r.length)return;let o=await jh(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await q_(t,e),a=kg(),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}})}U_({sinceMs:c,seen:[...i].slice(-H_)})}catch{}}import{execFile as J_}from"node:child_process";import{promisify as X_}from"node:util";import Sg from"node:fs/promises";import vg from"node:path";import K_ from"node:os";var Tg=process.env.BOSTRAT_STATE_DIR||vg.join(process.env.HOME||K_.tmpdir(),".bostrat"),G_=vg.join(Tg,"github-latency.ndjson"),V_=100,za=[],fu=new Map;function Ja({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>V_&&za.shift();let u=fu.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)),fu.set(t,u),z_(l),l}async function z_(t){try{await Sg.mkdir(Tg,{recursive:!0}),await Sg.appendFile(G_,JSON.stringify(t)+`
147
+ `)}catch{}}function Ag(){let t={};for(let[e,n]of fu)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 Y_(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([jr("git",["-C",t,"rev-parse","HEAD"]),jr("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await R({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var jr=X_(J_),Z_={timeout:3e4,maxBuffer:8*1024*1024};async function Yt(t,e,n={}){let{caller:r="prs",...o}=n,s=await ae({cwd:t});Le(r);try{let{stdout:a}=await jr("gh",e,{cwd:t,...Z_,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw fn(a)&&pn(a),a}}var Xa=null;async function hu(){if(Xa&&Date.now()-Xa.at<6e4)return Xa.data;let t;try{Le("auth");let e=await jr("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"||e?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await co();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Xa={at:Date.now(),data:t},t}async function Q_(t){try{let{stdout:e}=await jr("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function _g(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 pu(t){let e=(t||[]).map(_g),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 wu="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Ig=wu.replace("statusCheckRollup,","");function gu(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Rg(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 $g(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 Og(t,e){let n=await Rg(t,e);return n===null?{...pu([]),unavailable:!0}:pu($g(n))}function Ya(t,e=pu(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 tI=9e4,Dr=new Map;function eI(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Dr.delete(s);continue}r.add(s),Dr.has(s)||Dr.set(s,n),o.mergeabilityPending=n-Dr.get(s)<tI}for(let o of Dr.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Dr.delete(o);return e}async function Lr(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 Yt(n.path,["pr","view",String(r),"--json",wu]);return Ya(JSON.parse(o))}catch(o){if(!gu(o))throw o;let s=await Yt(n.path,["pr","view",String(r),"--json",Ig]),a=JSON.parse(s);return Ya(a,await Og(n.path,a.headRefOid))}}var Eg=(t,e)=>t.number-e.number;async function Yo(t,{caller:e="prs"}={}){try{let n=await Yt(t,["pr","list","--json",wu,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>Ya(r)).sort(Eg)}catch(n){if(!gu(n))throw n;let r=await Yt(t,["pr","list","--json",Ig,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Ya(o,await Og(t,o.headRefOid))))).sort(Eg)}}var nI=3e4,rI=1500,Xo=new Map;function ke(){Xo.clear()}async function oI(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 Aa(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await _a(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function Cg(){let t=await hu();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await ve())await Q_(a.path)&&e.push(a);let n=Date.now(),r=Ha(n)>0,o=await Promise.all(e.map(async a=>{let i=Xo.get(a.path),c=i?.pending?rI:nI;if(i&&(r||n-i.at<c))return i.entry;try{let l=await tt(a.path).catch(()=>null),u=eI(a.path,await Yo(a.path,{caller:"tab"}),n);if(await oI(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&&Ja({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 Xo.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 Xo.keys())s.has(a)||Xo.delete(a);return{auth:t,repos:o}}async function Fe(t){let e=(await ve()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function Jn(t){return ao(t)}var Zo=new Set(["squash","merge","rebase"]);async function Fr(t,e,{method:n="squash"}={}){let r=await Fe(t);if(!Zo.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 Ui().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 Qo(t,e){let n=await Fe(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 lh(o,r).catch(()=>{}),{ok:!0,number:r}}async function yu(t,e){let n=await Fe(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 sI=2500,aI=9e4,iI=t=>new Promise(e=>setTimeout(e,t));function bu(t){return t.green&&!t.isDraft}function cI(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 Mg(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:bu(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Ng(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var mu=new Map;function Pg(){let t={};for(let[e,n]of mu)t[e]={...n};return t}async function Bg(t,{method:e="merge",onMerged:n=null}={}){let r=await Fe(t);if(!Zo.has(e))throw new Error(`invalid merge method: ${e}`);if(mu.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Yo(r.path)).filter(bu).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 mu.set(r.path,s),uI(s,o,{listPrs:()=>Yo(r.path),merge:a=>Fr(r.path,a,{method:e}),sleep:iI,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function lI(t,e){let n=Date.now()+(e.settleMaxMs??aI),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(sI)}}async function uI(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 lI(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(!bu(a)){t.skipped.push({number:a.number,title:a.title,reason:cI(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 ku(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await jr("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${be(l)}`)}await Y_(t);let o=n||"";if((await ae({cwd:t})).GH_TOKEN){let l=await Ui().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 Dg(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 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(!gu(i))throw i;let c=await Yt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Rg(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(be(i));o=$g(l).map(_g).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=dI(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Yt(n.path,["run","view",c,"--log-failed"]).then(u=>fI(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function dI(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function fI(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
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()}
154
+ `).find(n=>n.trim())||"gh failed"}var pI=6e3,Za=new Map;function xu(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 jg(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await D(t)}catch{return}if(xu(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Wa(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Za.get(t.id)||0)<pI)return;Za.set(t.id,o);let s;try{s=await Yo(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 Bo(i,a.number))){Za.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(()=>{}),Za.delete(t.id)}import mI from"node:fs/promises";import Wg from"node:path";import hI from"node:os";import{randomUUID as qg}from"node:crypto";var wI=process.env.BOSTRAT_STATE_DIR||Wg.join(process.env.HOME||hI.tmpdir(),".bostrat"),Kg=Wg.join(wI,"flows.json"),Lg=20,Fg=80,Ug=500,Hg=80,Ue=null,Su=null;async function He(){return Ue||(Su||=(async()=>{let t=!0;try{Ue=JSON.parse(await mI.readFile(Kg,"utf8"))}catch{Ue={flows:[]},t=!1}finally{Su=null}if(Ue.flows||=[],!t&&pt()){let{records:e,source:n}=await Tt("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 Ur().catch(()=>{})}}return Ue})(),Su)}async function Ur(){await Mt(Kg,Ue)}var Qa=t=>Rt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function vu(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 Gg({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>Lg)throw new Error(`steps must be an array of 1-${Lg}`);let r=await Nn({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>Ug)throw new Error(`${l}: caption over ${Ug} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Hg)throw new Error(`${l}: title over ${Hg} 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:qg().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 gI(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Fg)throw new Error(`name over ${Fg} chars`);return t.trim()}async function Vg({trackId:t}={}){let e=await He();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function yI(t){let n=(await He()).flows.find(r=>r.id===t);return n?{...n}:null}async function zg(t){let e=await yI(t);if(!e)return null;let n=new Map((await Nn({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 Jg({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=gI(e),i=await He(),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 Gg({trackId:t,steps:n}),f=Date.now(),d={id:qg().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 Ur(),await vu(d,"created"),Qa(d),{...d}}async function Xg(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 Gg({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 Ur(),await vu(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Qa(o),{...o}}async function Yg(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 Ur(),await vu(r,"deleted"),Qt("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Zg(t){return(await He()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function Qg(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(),Qa(r);return n.length&&await Ur(),n.length}async function ty(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 Ur();for(let r of n)Qt("flows",r.id).catch(()=>{})}return n.length}async function ey(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Tt("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await He();for(let r of n.flows)await Qa(r);return{pushed:n.flows.length,source:"plane"}}import Tu from"node:fs/promises";import ts from"node:path";import{randomUUID as bI}from"node:crypto";var ry=".bostrat/uploads",Au=t=>ts.join(t,ry),ny=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,kI=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),xI=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 Eu(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return kI.has(e)?"image":xI.has(e)?"doc":null}function SI(t){let r=ts.basename(String(t||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function oy(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>ny)throw new Error(`file too large \u2014 max ${Math.floor(ny/(1024*1024))} MB`);let r=SI(e),o=ts.extname(r).slice(1),s=Eu(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=bI().slice(0,8),i=Au(t);await Tu.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Tu.writeFile(ts.join(i,c),n,{mode:384}),await Tu.chmod(ts.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${ry}/${c}`,kind:s,size:n.length}}function sy(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var vI=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function _u(t){return t.role==="user"&&vI.test((t.text||"").trim())}function Hr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var EI=AI(TI),We=process.env.BOSTRAT_STATE_DIR||F.join(process.env.HOME||"/tmp",".bostrat"),my=F.join(We,"tracks.json"),_I=F.join(We,"events.jsonl"),Nu=F.join(We,"workspaces"),II=process.env.BOSTRAT_DEFAULT_REPO||null,ri={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"}},oi=Object.keys(ri);function RI(t,{skipPermissions:e=!0}={}){let n=ri[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 hy(t,e,{skipPermissions:n=!0}={}){let r=ri[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=ri[t];if(e)return process.env[e.envVar]||e.base}var Xn={at:0,value:null};async function Bu({maxAgeMs:t=6e4}={}){if(Xn.value&&Date.now()-Xn.at<t)return Xn.value;let e=await Promise.all(oi.map(async n=>[n,await Hs(ls(n))]));return Xn.value=Object.fromEntries(e),Xn.at=Date.now(),Xn.value}async function si(t,e=!0){let n=RI(t,{skipPermissions:e});if(!await Hs(n)){Xn.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.":ac[r]?`Install it on this node: ${ac[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 ai=".bostrat/handoff/latest.md";function $I(t,e=ai){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 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:
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 OI=/\(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 ay(t){return t.role==="user"&&OI.test((t.text||"").trim())}var ti=".bostrat/handoff/subtask-result.md",ns=".bostrat/handoff/lead-context.md",CI=(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 ${ns} \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 ${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:
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 ${ti} (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 ${ns} \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 ${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()}
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 ${ti} 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.`,MI=(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,Iu=null;async function W(){return xe||(Iu||=(async()=>{try{xe=JSON.parse(await L.readFile(my,"utf8"))}catch{xe={tracks:[]}}finally{Iu=null}return xe})(),Iu)}async function z(){await Mt(my,xe)}var et=(t,e)=>t.tracks.find(n=>n.id===e)||null,Ru=Promise.resolve();function NI(t,e){let n=JSON.stringify({trackId:t,...e})+`
165
+ `;return Ru=Ru.then(()=>L.mkdir(We,{recursive:!0})).then(()=>L.appendFile(_I,n)).catch(()=>{}),Ru}var ct=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),NI(t.id,r),Ow(t,r),r};Uf(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&&(ct(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await z().catch(()=>{}))});var $u=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),PI=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function wy(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=>PI.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 gy(t,e,n,{kind:r}={}){let o=await Z(e);if(!o)throw new Error(`box "${e}" is not running`);let s=ls(o.agent);await tn({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await St(e,n,{..._e(o.agent),kind:r||"orchestration"}),await U({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);ct(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await z()}var Yn=new Set(["merged","conflict","canceled","failed","handed-off"]),iy=new Map;async function BI(t){let e=F.join(ea(t),`${t.id}.ndjson`),n;try{n=await L.stat(e)}catch{return null}let r=iy.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 iy.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function DI(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:tu(n.name,n.agent)}async function bn(t,e=null){let n=e??new Map((await Rn()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=Yn.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([BI(t),DI(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a}}var jI=5e3;function LI(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-jI,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 yy(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=Pf(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=LI(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 Ef(d.box,d.sessionId),n=!0}}return n}async function ot(){let t=await W();await yy(t)&&await z();let e=new Map((await Rn()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>bn(n,e)))}async function by(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 ky(t){let e=oo(t);if(!e)return null;let n=await W();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Md(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 yy(e)&&await z(),bn(n)):null}var FI={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function cy(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(FI[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 ly(t){let e=(t.text||"").trim();return e?UI.some(n=>n.test(e)):!1}var HI=10*6e4;async function Du(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=Mc(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...cm(n),...l],d={windows:[...vp(n),...Tp(n,oa),...a,...u]},m=(await Promise.all(n.boxes.map(k=>Rl(k,e)))).flat(),g=m.map(k=>{let V={kind:"message",...k};if(V.role==="assistant"&&(V.text||"").trim()&&V.tools?.length){let er=Wo(V.agent).cliTools,As=V.tools.filter(nr=>!er.has(nr));return{...V,tools:As.length?As:void 0}}return V}).map(k=>{if(k.role!=="user")return k;let V=Ka(k.text);return V?V.kind==="suppress"?null:{...k,note:V,text:mg(V)}:k}).filter(Boolean).filter(k=>!(k.role==="user"&&r.has((k.text||"").trim()))).filter(k=>!cy(k)).filter(k=>!ly(k)).filter(k=>!ay(k)).filter(k=>!_u(k)).filter(k=>k.role!=="assistant"||!!(k.text||"").trim()||!!k.images?.length).filter(k=>!Ap(k,d)),y=g.filter(k=>k.failed&&k.text===vl);if(y.length){let k=new Map;for(let V of y){if(!k.has(V.box)){let nr=[...(await ft(V.box,{lines:xt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Ni=>Ni[1]);k.set(V.box,nr.length?nr[nr.length-1]:null)}let er=k.get(V.box);er&&(V.text=`\u25A0 ${er}`)}}let b=await Promise.all(n.boxes.map(k=>zh(k,e)));await fg(n,b.flat()),await ug(n,g),await Cw(n),await hm(n,g).catch(()=>{});for(let k of n.boxes)k.agent==="codex"&&k.state==="live"&&await gg(n,k),k.agent==="cursor"&&k.state==="live"&&await xg(n,k);let S=await Promise.all(n.boxes.map(k=>Vh(k,e)));await dg(n,S.flat().filter(k=>!$u(k.box,k.ts,u)));let E=await D(n),M=c&&i.viewId?i.viewId:null,J=Hw(E),$=Jo(E).filter(k=>!(k.kind==="message"&&(cy(k)||ly(k)||ay(k)||_u(k)))&&!(k.kind==="view-card"&&M&&k.viewId===M)),j=Ww($,J),v=$.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&!j.has(k)&&$u(k.box,k.ts,J))),C=iu(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=>!$u(k.anchorBox,k.anchorTs??k.ts,u)),q=[...v,...C].filter(k=>k.ts!=null).sort((k,V)=>k.ts-V.ts),rt=WI(n),st=rt.size?q.filter(k=>k.kind==="event"||!rt.has(k.box??k.anchorBox)):q,w=n.boxes.find(k=>k.name===n.leadBox)||null,h=w?await bo(w.cwd,w.sessionId,{fallbackNewest:!1}):null;if(!h&&w){let k=[...E].reverse().find(V=>V.type==="context.usage"&&V.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 ft(w.name,{lines:xt}).catch(()=>""):"",G=w&&Cr(w.agent,A)?await Ze(w.name).catch(()=>A):A,K=w?Vn(w,G):null;!K&&w&&!Ee(w.agent,A)&&!uy(n.id).length&&(K=Fl(st,{leadBoxId:w.name,provider:w.agent,isAnswered:k=>E.some(V=>V.type==="prompt.answered"&&V.payload?.promptId===k)})),K&&Mw(n,w,K);let dt=st.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&k.ask&&!String(k.text||"").trim()&&!k.images?.length)),yt=!!w&&!K&&Ee(w.agent,A);w&&!w.readyAt&&(yt||K||A&&Xs(w.agent,A))&&(w.readyAt=Date.now(),await z());let Ot=!!w&&!w.readyAt&&Date.now()-(n.createdAt||0)<HI;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&w&&Xs(w.agent,A)&&!K){let k=await W(),V=et(k,n.id);V?.setup&&!V.setup.connectedAt&&(V.setup.connectedAt=Date.now(),n.setup.connectedAt=V.setup.connectedAt,await R(V,{type:"provider.connected",actor:"system",source:"pane",boxId:w.name,provider:w.agent,payload:{provider:w.agent,ts:V.setup.connectedAt}}).catch(()=>{}),await z())}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 V=da(n.id);V&&(ct(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:V.finishedAt}),await z())}else if(K||k>am&&!yt&&!i.viewId||k>Cc){let V=K?"needs-input":k>Cc?"timeout":"no-view";da(n.id,{error:V})&&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:V}}).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:Ew(w?.agent)},pendingPrompt:K,working:yt,stopping:JI(n.id),leadStarting:Ot,handoff:Vt,view:Zt,queued:uy(n.id).filter(k=>k.kind!=="ship-notify"&&k.kind!=="prompt-answer")}}function WI(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 xy(t,e){let n=await Q(t);if(!n)return null;let r=zr(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=Jo(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=iu(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 ft(o,{lines:xt}).catch(()=>"");u=Ee(l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function ju(t,{promptId:e,answers:n,text:r}={}){let o=et(await W(),t);if(!o)throw new Error(`track "${t}" not found`);Uu(t,"prompt-answer");let s=o.boxes.find(d=>d.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await Z(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Bl(s);if(i||(i=await qI(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 St(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=fw(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=sw(i,n||[]);await bt(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=zf(i,n||[]);await St(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 U({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 U({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 qI(t,e){if(!e)return null;let n=await D(t).catch(()=>[]);return Fl(Jo(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=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 Bl(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=aw(a,n);return c.length?(await bt(r.leadBox,c,{kind:"navigate-prompt"}),await U({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 KI=1500,rs=new Map;function Lu(t){let e=rs.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},rs.set(t,e)),e}function uy(t){return(rs.get(t)?.items??[]).map(e=>({...e}))}function ii(t){let e=rs.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 vy(t){let e=Lu(t);e.timer||(e.timer=setInterval(()=>GI(t).catch(()=>{}),KI))}async function GI(t){let e=rs.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){ii(t);return}let r=us(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Hr(e,!1);return}let o=await Z(n.leadBox);if(!o){Hr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await ft(n.leadBox,{lines:xt}).catch(()=>"");if(Js(s?.agent,a)){Hr(e,!1);return}if(Cr(s?.agent,a)){let c=await Ze(n.leadBox).catch(()=>a);if(Vn(s,c)){Hr(e,!1);return}}if(!Hr(e,!0))return;let i=e.items.shift();e.idleSince=null,await Ty(n,s,o,i.text,i.kind??null,i.attachments??null),await z()}finally{e.flushing=!1}}}async function Ty(t,e,n,r,o=null,s=null){let a=ei.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=$I(r,t.pendingHandoffHydrate.artifact||ai),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=sy(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
166
166
 
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}
167
+ ${f}`:f}let u=ct(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...l.length?{attachments:l}:{}});delete t.draft;try{if(await tn({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:ls(n.agent)}),(ei.get(t.id)||0)!==a){!o&&r.trim()&&(t.draft={text:[t.draft?.text,r].filter(Boolean).join(`
168
168
 
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(`
169
+ `),updatedAt:Date.now()},await z());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}await St(t.leadBox,i,{..._e(e?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){throw ct(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,...f?.interrupted?{reason:"interrupted"}:{}}),f}await aR(t,e,i)||(await Ry(t.leadBox,e?.agent,i.length+8).catch(()=>{}),ct(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,reason:"unsubmitted"}),o||(t.draft={text:r,updatedAt:Date.now()})),c&&await U({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function Ay(t,{actions:e}={}){Lu(t).items.push({id:cs(),text:qw(e),ts:Date.now(),kind:"ship-notify"}),vy(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"||!Mr(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`);Uu(t,"say");let a=await Z(s.leadBox);if(!a&&await qu(s)&&(await z(),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=Mr(e),l=Lu(t),u=await ft(s.leadBox,{lines:xt}).catch(()=>""),f=l.items.length>0||Js(i?.agent,u);if(!f&&Cr(i?.agent,u)){let d=await Ze(s.leadBox).catch(()=>u);f=!!Vn(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),vy(t),delete s.draft,await z(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await Ty(s,i,a,c,n,o),await z(),{ok:!0,leadBox:s.leadBox}}var dy="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 Ey(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=Mc(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";im(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${dy}
170
170
 
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}
171
+ What to preview: ${String(e).trim()}`:dy;try{await oe(t,a,{kind:"spin-up-view"})}catch(i){throw da(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Fu(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`);ei.set(t,(ei.get(t)||0)+1);let n=e.boxes.find(g=>g.name===e.leadBox),r=ii(t),o=os.get(t);if(o&&!o.canceled){o.clicks++;let g=r.filter(b=>b.text&&!b.kind).map(b=>b.text),y=null;return g.length&&(y=[e.draft?.text,...g].filter(Boolean).join(`
172
+
173
+ `),e.draft={text:y,updatedAt:Date.now()},await z()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:r.length,restored:y}}let s=Date.now(),a=await Z(e.leadBox),i=a?await QI(e.leadBox,n?.agent):{stopped:!1,sends:0},c=a&&i.stopped?await nR(e,n):null,l=!c&&a&&i.stopped?await Iy(e,n):null,u=a?await tR(e).catch(()=>null):null,f=r.filter(g=>g.text&&!g.kind).map(g=>g.text),d=c||l;f.length&&(d=[d,...f].filter(Boolean).join(`
174
+
175
+ `),e.draft={text:d,updatedAt:Date.now()},await z());let p={dropped:r.length,escSends:i.sends,unsent:!!c,lostRestored:!!l,queuedRestored:f.length,shipDismissed:!!u},m=!!a&&!i.stopped;return m?XI(e,n,{startedAt:s,syncSends:i.sends,outcome:p}):await _y(e,n,{ts:s,result:a?"stopped":"no-session",...p,clicks:1,bgSends:0,durationMs:Date.now()-s}),await U({flow:"interrupt",phase:m?"failed":"done",trackId:t,sessionName:e.leadBox,agent:n?.agent,stopped:i.stopped,stopping:m,...p}).catch(()=>{}),{ok:!0,interrupted:!!a,stopped:i.stopped,stopping:m,dropped:r.length,restored:d||null}}var VI=1e3,zI=12,os=new Map,ei=new Map;function JI(t){let e=os.get(t);return!!e&&!e.canceled}function Uu(t,e){let n=os.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function XI(t,e,{startedAt:n,syncSends:r,outcome:o}){let s={startedAt:n,sends:0,clicks:1,canceled:!1,cancelReason:null};os.set(t.id,s),(async()=>{let a=!1;for(let l=0;l<zI&&!s.canceled&&(await Zn(VI),!s.canceled);l++){let u=await ft(t.leadBox,{lines:xt}).catch(()=>"");if(!Ee(e?.agent,u)){a=!0;break}await bt(t.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),s.sends++}let i=!1;if(a&&!s.canceled)try{let l=et(await W(),t.id),u=l?.boxes.find(f=>f.name===l.leadBox);l&&u&&(i=!!await Iy(l,u))}catch{}let c=s.canceled?"canceled":a?"stopped-late":"gave-up";await _y(t,e,{ts:n,result:c,...o,lostRestored:o.lostRestored||i,escSends:r,bgSends:s.sends,clicks:s.clicks,durationMs:Date.now()-n,...s.cancelReason?{cancelReason:s.cancelReason}:{}}),await U({flow:"interrupt",phase:c==="gave-up"?"failed":"done",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,result:c,bgSends:s.sends,clicks:s.clicks}).catch(()=>{})})().catch(()=>{}).finally(()=>{os.delete(t.id)})}async function _y(t,e,n){await R(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}var YI=[300,350,400],ZI=3;async function QI(t,e){let n=0;for(let r=0;r<ZI;r++){await bt(t,["Escape"],{kind:"interrupt"}),n++;for(let o of YI){await Zn(o);let s=await ft(t,{lines:xt}).catch(()=>"");if(!Ee(e,s))return{stopped:!0,sends:n}}}return{stopped:!1,sends:n}}async function tR(t){let e=xu(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 eR=[250,400,500,600];async function nR(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 eR){await Zn(r);let o=await ft(t.leadBox,{lines:xt}).catch(()=>""),s=qo(e.agent,o);if(s&&Dl(n.text,s))return await Ry(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 z(),n.text}return null}var rR=3e4,oR=[350,650,900];async function Iy(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>rR||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!qt(r.text))return null;for(let s of oR){await Zn(s);let a;try{a=await Rl(e,{})}catch{return null}if(cw(a,r))return null;let i=await ft(t.leadBox,{lines:xt}).catch(()=>"");if(qo(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 z(),r.text}var sR=[250,450,700],Ou=2;async function aR(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=Ou;r++){let o=!1;for(let s of sR){await Zn(s);let a=await ft(t.leadBox,{lines:xt}).catch(()=>""),i=lw(e.agent,n,a,{busy:Ee(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Ou&&(await bt(t.leadBox,["Enter"],{kind:"submit-retry"}),U({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return U({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:Ou,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function Ry(t,e,n){for(let r=0;r<3;r++){await qs(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Zn(200);let o=await ft(t,{lines:xt}).catch(()=>""),s=qo(e,o);if(!s)return!0;n=s.length+8}return!1}async function $y(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=Mr(e);return r?n.draft={text:r,updatedAt:Date.now()}:delete n.draft,await z(),{ok:!0,draft:n.draft||null}}async function Oy(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=_w(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await bo(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await tn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ls(o.agent)});let u=await Iw(a,r.leadBox,i);return ct(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await z(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function Cy(t){for(let e of t){if(!e||!await Na(e))continue;let n=await hw(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 zo(t){let e=[];for(let n of Dt(t?.workspace)){if(!n?.dir)continue;let r=await ql(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Kl(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"&&!F.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await ao(e)??await Wi(e));let n=await Cy([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 iR(t,e,n,r){let o=`track/${Vu(e)}-${t}`,s=[];try{await L.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await My(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 Pa(c);await Nr(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await ci(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 Ny=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 Py(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 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=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 Pa(s);await ww(s,o.branch)?await gw(s,l,o.branch):await Nr(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 ci(o.dir,o.repos,o.branch),ct(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Ny(o)}),await z(),f}async function By(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 ql(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Kl(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 ci(o.dir,o.repos,o.branch),ct(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Ny(o)}),await z(),{removed:s.name}}async function cR(t,e,{repo:n,repos:r,cwd:o}={}){await L.mkdir(Nu,{recursive:!0});let s=F.join(Nu,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return iR(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!F.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await ao(n)??await Wi(n));let i=await Cy([n,o,II]);if(i){let c=`track/${Vu(e)}-${t}`;try{let l=await Pa(i);await Nr(i,s,c,l),await Pu(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 yw(s),await Pu(s),{dir:s,base:null,branch:null,kind:"repo"}}var ss="# bostrat workspace",Dy=`${ss}
172
176
 
173
177
  TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
174
178
  before substantive work \u2014 it is the shared cross-provider memory for this repo
@@ -335,9 +339,9 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
335
339
  statement, or omitted entirely renders no controls \u2014 so close EVERY completed
336
340
  implementation turn this way, every time. When the operator has already asked you
337
341
  to commit / push / open the PR, just do it; do not re-offer.
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}
342
+ `,lR=Dy,es=Dy+pw;async function Pu(t){try{await L.writeFile(F.join(t,"AGENTS.md"),es,{flag:"wx"})}catch{return}await fR(t,["/AGENTS.md"])}var Cu=t=>typeof t=="string"&&(t===ss||t.startsWith(`${ss}
343
+ `)),uR="# bostrat multi-repo workspace";function dR(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
344
+ `);return`${uR}
341
345
 
342
346
  This directory is NOT a git repo. It is a bostrat Track workspace holding one
343
347
  git worktree per tagged repo, ALL on the same branch \`${e}\`:
@@ -349,25 +353,25 @@ push / open PRs) in each repo separately, always on that shared branch. Work
349
353
  only needs to land in the repos it actually touches \u2014 an untouched repo's
350
354
  branch simply stays at its fork point and never gets pushed. When asked to run
351
355
  or preview an app, run it from that app's own repo directory.
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}
356
+ `}async function ci(t,e,n){let r=dR(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await L.writeFile(F.join(t,o),r).catch(()=>{})}var Mu=es.replace(`${ss}
357
+ `,`${ss}
354
358
 
355
359
  (Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
356
360
  SCOPE: these instructions apply ONLY when your working directory is under
357
361
  ~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
358
362
  When working anywhere else, ignore everything below.
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(`
363
+ `);async function jy(t){let{stdout:e}=await EI("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return F.resolve(t,e.trim())}async function fR(t,e){try{let n=await jy(t);await L.mkdir(F.dirname(n),{recursive:!0});let r=await L.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
360
364
  `).includes(s));o.length&&await L.appendFile(n,(r.endsWith(`
361
365
  `)||!r?"":`
362
366
  `)+o.join(`
363
367
  `)+`
364
- `)}catch{}}async function zI(t,e){try{let n=await $y(t),r=await L.readFile(n,"utf8"),o=r.split(`
368
+ `)}catch{}}async function pR(t,e){try{let n=await jy(t),r=await L.readFile(n,"utf8"),o=r.split(`
365
369
  `).filter(s=>s!==e);o.length!==r.split(`
366
370
  `).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(`
371
+ `))}catch{}}async function Ly(){let t={refreshed:0,removed:0,codexGlobal:null};try{await L.mkdir(We,{recursive:!0}),await L.writeFile(F.join(We,"CLAUDE.md"),lR).catch(()=>{}),await L.writeFile(F.join(We,"AGENTS.md"),es).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,Mu,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Cu(o)&&o!==Mu&&(await L.writeFile(r,Mu).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Nu,Kr]){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&&Cu(c)&&c!==es&&(await L.writeFile(i,es).catch(()=>{}),t.refreshed++);let l=F.join(a,"CLAUDE.md"),u=await L.readFile(l,"utf8").catch(()=>null);u!==null&&Cu(u)&&(await L.rm(l,{force:!0}).catch(()=>{}),await pR(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function mR(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 Hu({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 si(o,s),i=await W(),c=cs().slice(0,8),l=await cR(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(ic(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await $n(f,l.dir,{agentCmd:a,agent:o});Ys({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:[Jy(d,"lead","live")],events:[],createdAt:Date.now()};return ct(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 z(),bn(p)}var hR=new Set(["claude","codex","cursor"]);async function Fy({provider:t="claude",fresh:e=!1}={}){if(!hR.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 bn(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await Vr(r.id).catch(()=>{})}let o=(await si(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=cs().slice(0,8),a=F.join(We,"setup",s);await L.mkdir(a,{recursive:!0});let i=await $n(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&Ys({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:[Jy(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 W()).tracks.push(c),await z(),bn(c)}async function Uy(t,e,n){await mm(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 zr(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Ju(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 Hy(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 Jp(s),await Uy(t,s,"track deleted"),n?await Em(t).catch(()=>{}):await Am(t).catch(()=>{}),await $w(),await mR(s),e&&await Xp(t).catch(()=>{}),e?await ty(t).catch(()=>{}):await Qg(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 z(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}async function Vr(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(),ct(n,"archived",{boxes:n.boxes.length}),await z(),n.kind==="setup"?await Wy(t):gR(t).catch(()=>{}),bn(n)}var wR=9e4;async function gR(t){try{await yR(t)}catch{}await Wy(t).catch(()=>{})}async function Wy(t){let e=await W(),n=et(e,t);if(!(!n||!n.archivedAt)){await Uy(t,n,"track archived");for(let r of n.boxes)Yn.has(r.state)||(r.state="stopped");await z()}}async function yR(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 xp(n.leadBox,i.cwd,{timeoutMs:wR})),s="lead-cli"}catch{}if(r||(r=ll(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 z()}async function qy(){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=ll(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 z(),{recorded:e,skipped:n,empty:r}}async function hn(t,e){let n=await $t(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Ky(n);if(r.length){for(let o of r)await Tr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Vr(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Ky(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 wn(t,e){return Wu(t,e,"merged")}async function Wu(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 Ky(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await fh(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await oe(r.trackId,bR(a),{kind:"merge-notify"}),!0}catch{return!1}}var fy={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function bR(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 ${fy[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 ${fy[i.outcome]??i.outcome}`).join(`
368
372
  `),a=n.length?r.length?`This is an automated merge notification: pick up whatever follow-up work the merged PR${n.length===1?"":"s"} unblock${n.length===1?"s":""} \u2014 and note the unmerged one${r.length===1?"":"s"}: that work did NOT land, so check whether it still needs to ship. If nothing needs doing, say so briefly.`:"This is an automated merge notification: pick up whatever follow-up work these merges unblock (dependent tasks, cleanup, next steps that were waiting on them). If nothing needs doing, say so briefly.":"This is an automated notification: NONE of the PRs landed \u2014 the work did not ship. Check whether it still needs to go out another way. If not, say so briefly.";return`${o}
369
373
  ${s}
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}
374
+ ${a}`}async function qu(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||Yn.has(e.state)||await Z(e.name))return!1;let n=hy(e.agent,e.sessionId);if(!await Hs(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),Ys({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 Gy(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,ct(n,"unarchived",{}),await qu(n),await z(),bn(n)}async function Vy(){let t=await W(),e=new Set((await Rn()).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),!Yn.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await z(),r}async function zy(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(Yn.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await Z(n.leadBox)){let a=await ft(n.leadBox,{lines:xt}).catch(()=>"");if(Js(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 qu(n);if(await z(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return bn(n)}var Jy=(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 Xy(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)||(xe?et(xe,t)?.handoff:null)||null;async function ni(t,e){let n=await W(),r=et(n,t);r&&(r.handoff={...r.handoff||{},...e},await z())}function kR(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 Yy(){let t=await W(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=kR(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=Xy(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:ai,at:u},ct(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 U({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 U({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 z(),e}async function Ku(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=et(await W(),t);if(!s)throw new Error(`track "${t}" not found`);Uu(t,"handoff");let a=await si(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:wy(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return as.set(t,c),await ni(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),xR(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 ni(t,{step:c.step,error:c.error,finishedAt:c.finishedAt}).catch(()=>{}),U({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),U({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function xR(t,e,n,r,o){let s=async(E,M={})=>{t.step=E,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${E}`),await ni(t.trackId,{step:E,toBox:t.toBox}),await U({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 Ip(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await dl(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await tn({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:ls(o)});let d=await ft(t.fromBox,{lines:xt}).catch(()=>"");if(!Xs(o,d)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let E=hy(c,l,{skipPermissions:t.skipPermissions});await dl(t.fromBox,{agentCmd:E,agent:c,resumeSessionId:l,pinned:u});let M=await W(),J=et(M,t.trackId);if(J){let $=J.boxes.find(j=>j.name===t.fromBox);$&&($.state="live"),ct(J,"lead-handoff-failed",{box:t.fromBox,fromAgent:c,toAgent:o,reason:t.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:t.startedAt}),await z()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let 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=Xy(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:ai,at:y},ct(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 z(),await s("done",{sessionName:t.fromBox}),await ni(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var SR=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,vR=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Kr=F.join(We,"worktrees"),Wr=[ns,ti],re=new Map;var zr=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)},Gu=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 TR(t,e,n){let r=await W(),o=et(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await z())}var Gr=t=>TR(t.trackId,t.id,Gu(t));async function qr(t,e){t.step=e,await Gr(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 ER(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function _R(t,e,n){if(!ER(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),nb(s,o).catch(a=>tb(s,a)),!0}async function Zy(){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 _R(n,r,o)){e=!0,ct(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await U({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&&!Yn.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 U({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await py(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Yn.has(r.state)||re.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?F.join(Kr,`${n.id}-${r.subtaskId}`):null;await U({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await py(n,r.name,o)}}return e=await IR(t)||e,e&&await z(),e}async function py(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 IR(t){let e;try{e=await L.readdir(Kr)}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(Kr,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 U({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Vu=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",is=class extends Error{};async function Qy(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Mr(n))throw new Error("task (string) required");let s=await si(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 Na(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 Na(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:Mr(n),skipPermissions:r,reason:wy(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${Vu(a.name)}-${u}`,base:null,repos:l,dir:F.join(Kr,`${t}-${u}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return re.set(u,f),await Gr(f),RR(f,i,s).catch(d=>tb(f,d)),f}async function tb(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 Ju(t).catch(()=>{}),await Gr(t).catch(()=>{}),await MR(t).catch(()=>{})}function zu(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 eb(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 RR(t,e,n){if(await qr(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await Go(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await Gr(t)}else t.base=await Go(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await qr(t,"spawn"),await L.mkdir(Kr,{recursive:!0}),t.repos){await L.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Nr(a.leadDir,F.join(t.dir,a.name),t.branch,a.base);let s=Dt((et(await W(),t.trackId)||{}).workspace);await ci(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 Nr(e.cwd,t.dir,t.branch,t.base),await Pu(t.dir);await $R(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 OR(t,o),await gy(t.trackId,o.name,CI(t.task,!!t.repos),{kind:"subtask"}),await nb(t,e)}async function nb(t,e){await qr(t,"working");let n=F.join(t.dir,ti);if(await PR(n,t),t.canceled)throw new is;if(await qr(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 Wl(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await Vl(c.leadDir,c.base,t.branch,Wr),c.files=await zl(c.leadDir,c.base,t.branch,Wr);let u=await Jl(c.leadDir,c.base,t.branch,Wr);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 Wl(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Vl(e.cwd,t.base,t.branch,Wr),t.files=await zl(e.cwd,t.base,t.branch,Wr);let o=await Jl(e.cwd,t.base,t.branch,Wr);t.applied=o.applied,t.conflicts=o.conflicts}await Gr(t);let r=await NR(t,e,n);await qr(t,"resume"),await gy(t.trackId,t.leadBox,MI(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await qr(t,"reap"),await Ju(t),t.step="done",t.finishedAt=Date.now(),await CR(t),await Gr(t)}async function $R(t,e){let n=F.join(t.dir,F.dirname(ns));await L.mkdir(n,{recursive:!0});let r="";try{r=await Df(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
375
 
372
376
  **Delegated task:** ${t.task}
373
377
 
@@ -378,50 +382,50 @@ ${a}`}async function Lu(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name
378
382
  ## Conversation so far (lead \u2192 you)
379
383
 
380
384
  ${r}
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(`
385
+ `;await L.writeFile(F.join(t.dir,ns),o)}async function OR(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: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]||{},...Gu(t)},await z())}async function CR(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),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 z()}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"),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]||{},...Gu(t)},await z()}async function NR(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 Ju(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 PR(t,e){let n=(e.startedAt||Date.now())+SR;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 Zn(vR)}}function Zn(t){return new Promise(e=>setTimeout(e,t))}import ds from"node:fs/promises";import di from"node:path";import FR from"node:os";import BR from"ws";var rb=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function li(t,e,n={}){let r=await fetch(`${rb()}/${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 Xu=(t,e)=>li("chat.postMessage",t,e),Jr=(t,e)=>li("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 DR(t,e,n={}){let r=await fetch(`${rb()}/${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 ob(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await DR("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 li("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var jR=2200;function sb({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 li("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new BR(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()},jR);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 cb=()=>process.env.BOSTRAT_STATE_DIR||di.join(process.env.HOME||FR.tmpdir(),".bostrat"),ed=()=>di.join(cb(),"slack.json"),ui=null,Yu=null,Zu=Promise.resolve();async function rd(){return ui||(Yu??=ds.readFile(ed(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>ui=t).finally(()=>Yu=null),Yu)}async function ab(){let t=JSON.stringify({threads:ui},null,2),e=`${ed()}.${process.pid}.tmp`;await ds.mkdir(cb(),{recursive:!0}),await ds.writeFile(e,t),await ds.rename(e,ed())}async function qe(){Zu=Zu.then(ab,ab),await Zu.catch(()=>{})}async function od(t){return(await rd())[t]??null}async function UR(t,e){let n=await rd();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 fs(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function HR(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return fs(n.replace(/\n+/g," "),e)}function lb(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(fs(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
386
+ ${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${xa(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function nd(t,e){await Jr(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:lb(e)})}async function tr(t,e,n,r=void 0){return Xu(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var ib=new Set;function Qn(t){ib.add(t),t.catch(()=>{}).finally(()=>ib.delete(t))}var ut=t=>({response_type:"ephemeral",text:t}),WR="*/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._",qR=["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 td=(t,e)=>ut(`No live Track matches \u201C${Y(e)}\u201D. Live: ${t.map(n=>`\`${Y(n.name)}\``).join(" \xB7 ")||"none"}`);async function ub(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?di.basename(c.base):"fresh workspace"),lead:"claude"},u=await Xu(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:lb(l),unfurl_links:!1});l.ts=u.ts,(await rd())[i.id]=l,await qe(),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 KR(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await od(n.id);s&&(s.archivedAt=Date.now(),await qe(),await nd(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 GR(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 VR(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 zR(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 *${Y(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function JR(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?di.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
387
  `)+(o?`
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">
388
+ _${o} archived_`:"")}async function XR(t,e){let n=await jn();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?(Qn(ub(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:zR(u,i)}}case"archive":{if(!a.length)return ut("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Qu(await r.listTracks(),a.join(" "));return i?(Qn(KR(t,n,{track:i,responseUrl:e.response_url})),ut(`Archiving *${Y(i.name)}*\u2026`)):td(c,a.join(" "))}case"handoff":{let i=r.knownAgents??qR,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}=Qu(await r.listTracks(),l);return u?(Qn(GR(t,n,{track:u,agent:c,responseUrl:e.response_url})),ut(`Handing *${Y(u.name)}* to ${Y(c)}\u2026`)):td(f,l)}case"stop":{if(!a.length)return ut("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Qu(await r.listTracks(),a.join(" "));return i?(Qn(VR(t,n,{track:i,responseUrl:e.response_url})),ut(`Stopping *${Y(i.name)}*\u2026`)):td(c,a.join(" "))}case"list":return ut(await JR(r));default:return ut(WR)}}async function YR(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await od(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 Jr(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(l||"question")}~
389
+ \u2713 *${Y(a)}*`}}]}).catch(()=>{})}}catch(c){await jt(n.response_url,ut(`Couldn't answer: ${c?.message||c}`))}}async function ZR(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,La(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 QR(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await jn();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}),Qn(ub(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"?Qn(YR(t,r,e,o)):n.action_id==="bostrat-ship"&&Qn(ZR(t,r,e,o))}async function t$(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 UR(e.channel,e.thread_ts);if(!n)return;let r=await jn();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await tr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function e$(t){return async e=>{if(e.type==="slash_commands")return XR(t,e.payload);if(e.type==="interactive"){await QR(t,e.payload);return}if(e.type==="events_api"){await t$(t,e.payload?.event);return}}}var n$=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 r$(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: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 o$(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await Jr(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 tr(t,e,r.question||"The lead needs an answer",r$(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await qe()}async function s$(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 Jr(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Y(s||"question")}~
390
+ \u2713 _answered_`}}]})}async function a$(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 tr(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Y(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 i$(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 Jr(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function c$(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??l$)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await ob(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 tr(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 l$(t,e){let n=await nn(t);if(!n)return null;let r=Ie(n,e);if(!r)return null;let o=await ds.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 u$(t,e){if(!n$.has(e.type))return;let n=await od(e.trackId);if(!n)return;let r=await jn();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 nd(r,n);return;case"turn.agent":{if(!o.text)return;let s=HR(o.text);if(!s)return;await tr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Y(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${xa(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return o$(r,n,e);case"prompt.answered":return s$(r,n,e);case"pr.offered":return a$(r,n,e);case"pr.offer.answered":return i$(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 tr(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 nd(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await tr(r,n,`Live preview ${o.name}: ${va(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Y(o.name)}*: ${va(o.slug)}
391
+ _runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return c$(r,n,e,t.deps)}}function db(t){let e={deps:t};Jt.on("event",s=>{u$(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await jn()||(r&&clearInterval(r),r=null,n=sb({getToken:async()=>(await jn())?.appToken,onEnvelope:e$(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import d$ from"node:fs/promises";function f$(t){let e=new Map;for(let n of String(t||"").split(`
392
+ `)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&e.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...e.values()]}function p$(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 m$(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Ae(e.cwd,e.sessionId):e?.cwd?dc(e.cwd):null}async function fb(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 h$(r,o,t,i);break;default:break}return a}async function h$(t,e,n,r){let o=[],s=m$(n,e);if(s)try{o=f$(await d$.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=p$(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 y$ from"node:fs";import sd from"node:fs/promises";import b$ from"node:http";import Xr from"node:path";import pi from"node:os";import{createRequire as k$}from"node:module";import ad from"ws";import w$ from"node:path";import g$ from"node:os";var pb=process.env.BOSTRAT_STATE_DIR||w$.join(process.env.HOME||g$.tmpdir(),".bostrat");function mb(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 hb(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 fi=(()=>{try{return k$(import.meta.url)("../package.json").version||null}catch{return null}})(),x$=process.env.BOSTRAT_STATE_DIR||Xr.join(process.env.HOME||pi.tmpdir(),".bostrat");function S$(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 v$({home:t=process.env.HOME||pi.homedir(),env:e=process.env}={}){let n=r=>{try{return y$.statSync(r),!0}catch{return!1}};return{claude:n(Xr.join(t,".claude",".credentials.json")),codex:n(Xr.join(e.CODEX_HOME||Xr.join(t,".codex"),"auth.json"))}}var _t=1,wb=1e3,T$=3e4,A$=6e4,E$=1500,_$=500,gb=100,I$=2e3,R$=25e3;function yb({url:t,token:e,nodeId:n=pi.hostname(),name:r=pi.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=hb(t);let c=a.replace(/^http/,"ws"),l=new Map,u=new Map,f=Xr.join(x$,"plane-link","cursor.json"),d={},p=new Map,m=null,g=!1,y=!1,b=wb,S=null,E=null,M=null,J=null,$=!1;async function j(){try{d=JSON.parse(await sd.readFile(f,"utf8"))}catch{d={}}}function v(){$=!0,!M&&(M=setTimeout(async()=>{if(M=null,!!$){$=!1;try{await sd.mkdir(Xr.dirname(f),{recursive:!0}),await sd.writeFile(f,JSON.stringify(d))}catch(O){i.error("[plane-link] cursor flush failed:",O?.message||O)}}},I$),M.unref?.())}function C(O){return!m||m.readyState!==ad.OPEN?!1:(m.send(JSON.stringify(O)),!0)}function q(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 rt(){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 G=d[I.id]??0,K=A.filter(dt=>dt.seq>G&&typeof dt.id=="string");for(let dt=0;dt<K.length;dt+=gb){let yt=K.slice(dt,dt+gb).map(Ot=>({trackId:I.id,event:Ot}));if(!q(yt))return}}}function st(O){!g||!O?.trackId||typeof O.id!="string"||q([{trackId:O.trackId,event:O}])}let w=new Map;async function h(O){let{reqId:I,method:A,path:G,headers:K={},body:dt}=O,yt=Object.fromEntries(Object.entries(K).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+G,{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:G,method:K,path:dt,body:yt}=O,Ot=new URL(a);return new Promise(Vt=>{let Zt=b$.request({host:Ot.hostname,port:Ot.port,method:K,path:dt,headers:{...I,host:O.host},signal:A.signal},k=>{let{connection:V,"keep-alive":er,"transfer-encoding":As,upgrade:nr,...Ni}=k.headers;C({v:_t,ch:"control",type:"http_head",reqId:G,status:k.statusCode||502,headers:Ni}),k.on("data",nx=>{C({v:_t,ch:"control",type:"http_data",reqId:G,data:Buffer.from(nx).toString("base64")})||k.destroy()}),k.on("end",()=>{C({v:_t,ch:"control",type:"http_end",reqId:G,error:null}),Vt()}),k.on("error",()=>{C({v:_t,ch:"control",type:"http_end",reqId:G,error:"view upstream error"}),Vt()})});Zt.on("error",k=>{C({v:_t,ch:"control",type:"http_end",reqId:G,error:k?.message||"view request failed"}),Vt()}),yt&&Zt.write(Buffer.from(yt,"base64")),Zt.end()})}function _(){if(y)return;m=new ad(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:fi,providers:v$(),...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=wb,S=null,i.log(`[plane-link] connected to plane as ${n}`),A.minNodeVersion&&fi&&S$(fi,A.minNodeVersion)&&i.error(`[plane-link] this node is bostrat-node ${fi}; the plane asks for \u2265${A.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),J&&clearInterval(J),J=setInterval(()=>C({v:_t,ch:"ctl",type:"ping"}),R$),J.unref?.(),rt().catch(G=>i.error("[plane-link] backfill failed:",G?.message||G));return}if(A.ch==="events"&&A.type==="ack"&&Array.isArray(A.ids)){for(let G of A.ids){let K=p.get(G);K&&(p.delete(G),(d[K.trackId]??0)<K.seq&&(d[K.trackId]=K.seq))}v();return}if(A.ch==="control"&&A.type==="http_open"){h(A).catch(G=>C({v:_t,ch:"control",type:"http_end",reqId:A.reqId,error:G?.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 G=new ad(c+A.path,Array.isArray(A.protocols)?A.protocols:[],A.host?{headers:{host:A.host}}:void 0);l.set(A.streamId,G),u.set(A.streamId,[]),G.on("open",()=>{let dt=u.get(A.streamId)||[];u.delete(A.streamId);for(let{buf:yt,bin:Ot}of dt)G.send(yt,{binary:Ot})}),G.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 K=()=>{u.delete(A.streamId),l.delete(A.streamId)&&C({v:_t,ch:"attach",type:"close",streamId:A.streamId})};G.on("close",K),G.on("error",K);return}if(A.ch==="attach"&&A.type==="data"){let G=l.get(A.streamId);if(!G)return;let K=Buffer.from(A.data,"base64"),dt=A.bin===!0,yt=u.get(A.streamId);yt?yt.push({buf:K,bin:dt}):G.send(K,{binary:dt});return}if(A.ch==="attach"&&A.type==="close"){let G=l.get(A.streamId);l.delete(A.streamId),u.delete(A.streamId),G?.close();return}});let O=()=>{g&&i.log("[plane-link] disconnected from plane"),g=!1,J&&(clearInterval(J),J=null),p.clear();for(let G of l.values())G.close();l.clear(),u.clear();for(let G of w.values())G.abort();if(w.clear(),y||E)return;S==null&&(S=Date.now());let I=Date.now()-S<A$,A=I?E$+Math.random()*_$:b;E=setTimeout(()=>{E=null,_()},A),E.unref?.(),I||(b=Math.min(b*2,T$))};m.on("close",O),m.on("error",O)}return Jt.on("event",st),j().then(_),{stop:()=>{y=!0,Jt.off("event",st),E&&clearTimeout(E),M&&clearTimeout(M),J&&clearInterval(J),C({v:_t,ch:"ctl",type:"bye",reason:"shutdown"}),m?.close()},status:()=>({connected:g,nodeId:n,cursor:{...d},inflight:p.size})}}function cd(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 bb=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),kb=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},id=t=>Math.round(t*1e4)/1e4;async function xb(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=bb(),u=0,f=[];for(let d of t||[]){let p=await D(d),m=ue(p,{boxLabels:cd(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);kb(l,m.totalTokens);for(let[g,y]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:bb()},n[g].usd=id(n[g].usd+y.usd),n[g].events+=y.events,kb(n[g].tokens,y.tokens);for(let[g,y]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=id(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:id(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)}`,Yr=(t,e)=>String(t).padEnd(e);function Sb(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(` ${Yr(a,26)} ${Yr(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(` ${Yr(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(` ${Yr(c.label,40)} ${Yr(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))}${Yr(ps(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
393
+ `)}import N$ from"node:fs/promises";import P$ from"node:path";var mi=t=>t.payload?.ts??t.ts,$$=12e4,kn=t=>Math.round(t*1e4)/1e4,O$=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function C$(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>mi(S)>=e,a=pr([xo(n?.name),xo(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=pr(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)=>mi(S)-mi(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],J=mi(S);switch(E.lastTs!=null&&J>E.lastTs&&s(S)&&(M.activeMs+=Math.min(J-E.lastTs,$$)),E.lastTs=J,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(pr($));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&&!sa($.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??Qf(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,q]of Object.entries(M.models))j.models[C]=(j.models[C]||0)+q;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 Tb(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:J,sawCursorActivity:$,unpricedTurns:j}=C$(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:J};for(let[C,q]of S){v.usd+=q.usd,v.activeMs+=q.activeMs,v.turns+=q.turns,v.categories[C]={usd:kn(q.usd),activeMs:q.activeMs};for(let[w,h]of Object.entries(q.models))v.models[w]=kn((v.models[w]||0)+h);for(let[w,h]of Object.entries(q.providers))v.providers[w]=kn((v.providers[w]||0)+h);o.has(C)||o.set(C,O$());let rt=o.get(C);rt.usd+=q.usd,rt.activeMs+=q.activeMs,rt.turns+=q.turns,rt.toolCalls+=q.toolCalls;for(let[w,h]of Object.entries(q.models))rt.models[w]=(rt.models[w]||0)+h;for(let[w,h]of Object.entries(q.providers))rt.providers[w]=(rt.providers[w]||0)+h;let st=rt.byTrack.get(y?.id)||{usd:0,activeMs:0};st.usd+=q.usd,st.activeMs+=q.activeMs,rt.byTrack.set(y?.id,st)}(v.usd>0||v.activeMs>0||v.turns>0)&&(v.usd=kn(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:kn(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:vb(y.models),providers:vb(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:kn(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:kn(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},vb=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,kn(n)]));var Zr={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function B$(t){let e=String(t||"7d");return e in Zr?{key:e,ms:Zr[e]}:{key:"7d",ms:Zr["7d"]}}function D$(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=ja(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 j$(t){if(!t?.cwd)return null;try{let n=(await N$.readFile(P$.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}:B$(e),f=[];for(let d of t||[]){let p=await D(d),m=D$(d,p);i&&c<o&&(m=m.filter(g=>(g.ts??0)<=c)),f.push({track:d,events:m,brief:await j$(d)})}return{window:l,...Tb(f,{windowMs:u,now:c})}}var xn=36e5,Rb=24*xn,L$=400,F$=12,Qr=t=>Math.round(t*1e4)/1e4;function U$({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 Zr?String(t||"7d"):"7d",i=Zr[a];return{key:a,since:i==null?null:r-i,until:r}}var H$=(t,e)=>t!=null&&e-t<=48*xn?xn:Rb;function Eb(t,e){if(e===xn)return Math.floor(t/xn)*xn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function W$(t,e){if(e===xn)return t+xn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var _b=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Ib=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},q$=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Qr(n)]));async function $b(t,e={}){let n=e.now??Date.now(),r=U$({...e,now:n}),o=r.since==null?Rb:H$(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=_b(),f=0,d=0,p=0,m=0,g=0,y=new Set,b=new Map,S=[],E=[],M=[],J=null;for(let v of t||[]){let q=(await D(v)).filter(w=>w.type==="cost.recorded");if(!q.length){v.archivedAt||M.push(v.name||v.id);continue}let rt=[];for(let w of q){let h=w.ts??0;s(h)?rt.push(w):i(h)&&(g+=ia(w).usd)}if(!rt.length)continue;let st=ue(rt,{boxLabels:cd(v)});f+=st.totalUsd,d+=st.turns,p+=st.repricedTurns||0,m+=st.unpricedTurns||0;for(let w of st.unpricedModels||[])y.add(w);Ib(u,st.totalTokens);for(let[w,h]of Object.entries(st.byModel))c[w]||={usd:0,events:0,tokens:_b()},c[w].usd=Qr(c[w].usd+h.usd),c[w].events+=h.events,Ib(c[w].tokens,h.tokens);for(let[w,h]of Object.entries(st.byProvider))l[w]||={usd:0,events:0},l[w].usd=Qr(l[w].usd+h.usd),l[w].events+=h.events;for(let w of rt){let{usd:h}=ia(w);if(!h)continue;let T=Eb(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,_),(J==null||T<J)&&(J=T)}S.push({id:v.id,name:v.name||v.id,archived:!!v.archivedAt,usd:st.totalUsd,turns:st.turns,providers:Object.keys(st.byProvider)});for(let[w,h]of Object.entries(st.byBox))E.push({trackId:v.id,trackName:v.name||v.id,boxId:w,...h})}let $=r.since!=null?Eb(r.since,o):J,j=[];for(let v=$,C=0;v!=null&&v<r.until&&C<L$;v=W$(v,o),C++){let q=b.get(v);j.push(q?{t:v,total:Qr(q.total),byProvider:q$(q.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:Qr(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:M},prior:a!=null?{totalUsd:Qr(g),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:E.slice(0,F$)}}import to from"node:fs/promises";import K$ from"node:fs";import It from"node:path";import G$ from"node:os";import{execFile as V$}from"node:child_process";import{promisify as z$}from"node:util";var J$=z$(V$),Ob=process.env.BOSTRAT_STATE_DIR||It.join(process.env.HOME||G$.tmpdir(),".bostrat"),hi=!1,ld=null;async function ud(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(It.dirname(e),{recursive:!0}),await to.copyFile(t,e),!0)}var X$=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function wi(t,e,{skip:n=null}={}){let r=0,o=await to.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(X$.has(s.name)||n?.has(s.name))continue;let a=It.join(t,s.name);s.isDirectory()?r+=await wi(a,It.join(e,s.name)):s.isFile()&&(r+=await ud(a,It.join(e,s.name))?1:0)}return r}async function Cb({stateDir:t=Ob,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Ob}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await to.mkdir(e,{recursive:!0});let o=0;o+=await ud(It.join(t,"tracks.json"),It.join(e,"tracks.json"))?1:0,o+=await ud(It.join(t,"events.jsonl"),It.join(e,"events.jsonl"))?1:0,o+=await wi(It.join(t,"events"),It.join(e,"events")),o+=await wi(It.join(t,"memory"),It.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await wi(It.join(u.cwd,".bostrat"),It.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>J$("git",["-C",e,...u]);K$.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 Hi(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||hi))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,hi&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),hi=!1,ld=null}catch(u){hi=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
394
+ `).slice(-3).join(" | ").slice(0,300);f!==ld&&(ld=f,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${f}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as Y$}from"node:child_process";import{promisify as Z$}from"node:util";var Q$=Z$(Y$),tO=12e4,eO=6e4;function nO(t){return`refs/bostrat/wip/${t}`}function Pb(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function rO(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:tO}var gi=(t,e,n=15e3)=>Q$("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),Mb=new Map,Nb=new Set;async function oO(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await gi(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await gi(r,["remote","get-url","origin"])}catch{return Nb.has(r)||(Nb.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 Go(r,"bostrat: wip snapshot"),{stdout:s}=await gi(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Mb.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=nO(n);return await gi(r,["push","--force","origin",`${o}:${i}`],eO),Mb.set(r,a),await R({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
395
+ `)[0]}`),{pushed:!1,reason:"push-failed"}}}function Bb({listTracks:t,sweepMs:e=rO(),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 oO(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 sO}from"node:child_process";import{promisify as aO}from"node:util";import{randomUUID as iO}from"node:crypto";import cO from"node:fs";import lO from"node:os";import uO from"node:path";var vn=aO(sO),yi=t=>new Promise(e=>setTimeout(e,t)),Tn=rc,dd=process.env.BOSTRAT_USAGE_PROBE_CWD||uO.join(lO.homedir(),".bostrat","usage-probe"),dO=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,fO=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,pO=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Db=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Sn=null,ms=null,hs=null;async function mO(){try{return await vn(N,P("has-session","-t",Tn)),!0}catch{return!1}}function hO(){Sn&&clearTimeout(Sn),Sn=setTimeout(()=>wO().catch(()=>{}),dO),Sn.unref&&Sn.unref()}async function wO(){Sn&&(clearTimeout(Sn),Sn=null);try{await vn(N,P("kill-session","-t",Tn))}catch{}}async function gO(){let t=Date.now()+pO;for(;;){let e=await fd().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await yi(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await yi(500)}}async function yO(){if(await mO())return;if(!await Ye())throw new Error("host tmux server not running \u2014 start globo-tmux.service");cO.mkdirSync(dd,{recursive:!0}),await Ge(dd);let t=iO();await vn(N,P("new-session","-d","-s",Tn,"-c",dd,"-x","200","-y","60",...nc()));let e=`${ec} --session-id ${t}`;await vn(N,P("send-keys","-t",Tn,e,"Enter")),await gO()}async function fd(){let{stdout:t}=await vn(N,P("capture-pane","-t",Tn,"-p"));return t}async function bO(){await yO(),await vn(N,P("send-keys","-t",Tn,"-l","/usage")),await vn(N,P("send-keys","-t",Tn,"Enter"));let t=Date.now()+fO,e="";for(;await yi(400),e=await fd(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await yi(900),e=await fd(),await vn(N,P("send-keys","-t",Tn,"Escape")).catch(()=>{}),hO(),e}function kO(t){let e=String(t||"").split(`
396
+ `).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,e.length)&&!/^Current\s/i.test(e[c]);c++){let l=e[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=e[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=e.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return e.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function xO(t){let e=String(t||"").split(`
397
+ `).map(s=>s.replace(/\s+$/,"")),n=e.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<e.length&&!r.test(e[s]);s++)e[s].trim()&&o.push(e[s].trim());return o.length?o:null}async function jb({fresh:t=!1}={}){return!t&&ms&&Date.now()-ms.at<Db?ms.data:hs||(hs=(async()=>{try{let e=await bO(),n=kO(e),r={...n,contributors:xO(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()-(Db-3e3),data:n},n}finally{hs=null}})(),hs)}var SO=262144;function vO(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 Lb(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:vO(t.resets_in_seconds)}:null}function TO(t){let e=String(t||"").split(`
398
+ `),n=!1;for(let r=e.length-1;r>=0;r--){let o=e[r].trim();if(!o||o[0]!=="{"||!o.includes("token_count"))continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s?.payload;if(a?.type!=="token_count")continue;n=!0;let i=a.rate_limits;if(!i)return{session:null,week:null,sawTokenCount:n};if(i.primary||i.secondary)return{session:Lb(i.primary),week:Lb(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 pd(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Hn(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,SO),o=TO(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 Fb(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 bi({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 pd(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await pd(null))}return n.has("cursor")&&r.push(await bi({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function bi(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?pd(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await jb({fresh:e})}}function AO(t=process.env){return!!(zt(t)&&t.BOSTRAT_PLANE_TOKEN)}var EO=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),ki=(t,e)=>Object.assign(new Error(t),{status:e});async function Ub(t,e){if(!AO(e))throw ki("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${zt(e)}${t}`,{headers:EO(e)})}catch(o){throw ki(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw ki(r?.error||`plane replied ${n.status}`,n.status);return r}async function Hb(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await Ub(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function Wb(t,{env:e=process.env}={}){if(!t)throw ki("message id required",400);return(await Ub(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function md(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 _O(t){let[e,n]=t.split("/"),r=md(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 qb(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(_O).filter(Boolean)}function IO(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function Kb(t){let e=qb(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:IO,allowed(r){if(n)return!0;let o=md((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function Gb(t,e){let n=qb(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=md(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import Vb from"node:crypto";import xi from"node:fs";import RO from"node:path";function zb(t=process.env,{stateDir:e=pb,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=RO.join(e,"api-token");try{let a=xi.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=Vb.randomBytes(32).toString("base64url");try{xi.mkdirSync(e,{recursive:!0}),xi.writeFileSync(o,s+`
399
+ `,{mode:384}),xi.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function 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 Jb(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:Vb.timingSafeEqual(n,r)}function Xb(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 $O=[/^\/api\/github\/token\b/],OO=t=>$O.some(e=>e.test(t));function Yb({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=Xb(o);return Jb(c,t)?a():OO(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 Zb(t,e,{enforce:n=!0}={}){return!n||Jb(Xb(t),e)?!0:ws(t)}import CO from"node:http";import MO from"node:net";function Qb(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 Si(t){let e=Qb(t.headers.host);return e?{slug:e,view:Bc(e)}:null}var NO=t=>`<!doctype html><meta charset="utf-8">
396
400
  <meta http-equiv="refresh" content="3">
397
401
  <title>${t.name} \u2014 starting</title>
398
402
  <body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
399
403
  <div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
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
404
+ <p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function tk(t,e,n){let r=Si(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(NO(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=CO.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 ek(t,e,n){let r=Si(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
401
405
  \r
402
406
  bostrat: no live view "${s}"\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
407
+ `),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
404
408
  `)+`\r
405
409
  \r
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
410
+ `),n?.length&&a.write(n),e.pipe(a),a.pipe(e)}),i=()=>{a.destroy(),e.destroy()};return a.on("error",i),e.on("error",i),!0}function nk(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}.${PO(n.host)}/`}function PO(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 Qb(e)&&Bc(n[0])?n.slice(1).join("."):e}var rk=10,ok=200,BO=120,DO=24,jO=/^\d{4}-\d{2}-\d{2}$/;function hd(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var LO=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function sk(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 FO(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Ro(t);if(e.length)throw new Error(`changelog entry appears to contain a secret VALUE (${e.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of t.split(`
411
+ `)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,BO)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${DO}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>ok)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${ok}-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>rk)throw new Error(`${r.length} bullets \u2014 over the ${rk}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function ak(t,{repos:e=hd()}={}){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 ik(t,{md:e,day:n=null}){let r=FO(e),o=n==null||n===""?LO():String(n);if(!jO.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 UO}from"node:child_process";import{promisify as HO}from"node:util";var gd=HO(UO),ck=3e4,WO=30*6e4,qO=ck,KO=10*6e4,GO=10080*6e4,VO=60*6e4,zO=8,JO=1440*6e4,XO=3,lk="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",wd=60*6e4,YO=15*6e4,vi=new Map,Ti=new Map,ZO=/could not resolve|not found|no pull requests?/i,QO=t=>ZO.test(String(t?.message||t||""));function tC(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<wd)}var uk={fast:qO,slow:KO,decay:VO};function dk(t,e){return t.armedAt&&e-t.armedAt<WO||tC(t,e)?"fast":e-(t.firstSeen||0)>GO?"decay":"slow"}function eC(t,e){return uk[dk(t,e)]}function ks(t,e,n,r){let o=t.lanes?.get(H(e.repo,e.number))||{};return Ja({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function nC(t,e){Le("watch");try{let{stdout:n}=await gd("gh",["pr","view",String(e),"--repo",t,"--json",lk],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await ae({slug:t})}});return JSON.parse(n||"null")}catch(n){throw fn(n)&&pn(n),n}}async function rC(t){Le("watch");try{let{stdout:e}=await gd("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",lk],{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 fn(e)&&pn(e),e}}async function oC(){let t=new Map;for(let e of await ot().catch(()=>[]))t.set(e.id,e);return t}async function sC(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 aC(t,e){let n=await Jn(t);return n?Lr(n,e):null}async function iC(t,e,n){let r=await Jn(t);if(!r)throw new Error(`no local checkout for ${t}`);return Fr(r,e,{method:n})}async function cC(t,e){let n=await Jn(t);if(!n)throw new Error(`no local checkout for ${t}`);return Qo(n,e)}async function lC(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"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 uC=10*6e4;async function dC(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>JO){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<uC)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 fk(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function gs(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 fC(t,e){try{return await gd("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 D(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 pC(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 Aa(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await _a(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 dh(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await uh(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)>wd)await Ea(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 Ea(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 Ea(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>wd)await Ia(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Ia(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 Ia(t.repo,t.number).catch(()=>{})}}}}async function mC(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 hC(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 fk(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 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 wC(t={}){let{now:e=Date.now(),auth:n=hu,view:r=nC,list:o=rC,links:s=ph,trackMap:a=oC,emitMerged:i=sC,mark:c=cn,archiveIfArmed:l=hn,notifyIfArmed:u=wn,notifyIfResolved:f=Wu,batchThreshold:d=XO,detail:p=aC,merge:m=iC,updateBranch:g=cC,lookup:y=$t,disarm:b=jo,emitDisarmed:S=lC,localHasCommit:E=fC}=t;if(!(await n().catch(()=>null))?.authenticated||Ha()>0)return 0;let $=(await s().catch(()=>[])).filter(w=>e-(vi.get(H(w.repo,w.number))||0)>=eC(w,e));if(!$.length)return 0;let j=Date.now(),v=new Map,C=new Map;for(let w of $){let h=dk(w,e);v.set(H(w.repo,w.number),{lane:h,intervalMs:uk[h]}),vi.set(H(w.repo,w.number),e),C.has(w.repo)||C.set(w.repo,[]),C.get(w.repo).push(w)}let q=await a().catch(()=>new Map),rt={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){QO(I)&&_.add(H(O.repo,O.number))}}if(T)for(let O of h){let I=H(O.repo,O.number),A=T.get(Number(O.number));if(!A){if(_.has(I)){let G=(Ti.get(I)||0)+1;Ti.set(I,G),G>=zO&&(await hC(O,q,rt),Ti.delete(I),vi.delete(I))}continue}Ti.delete(I),A.state==="MERGED"||A.state==="CLOSED"?(A.state==="MERGED"?await fk(O,A,q,rt):await mC(O,A,q,rt),vi.delete(I)):(await pC(O,A,q,rt).catch(()=>{}),O.autoMerge&&await dC(O,q,rt).catch(()=>{}))}}let st={};for(let{lane:w}of v.values())st[w]=(st[w]||0)+1;return console.log(`[pr-watch] probed ${$.length} link(s) (${Object.entries(st).map(([w,h])=>`${w} ${h}`).join(", ")}) in ${Date.now()-j}ms`),$.length}function pk(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await wC()}catch{}finally{t=!1}}},ck)}import Ke from"node:fs/promises";import no from"node:path";import kk from"node:os";import mk from"node:fs/promises";import Ai from"node:path";import{execFile as gC}from"node:child_process";import{promisify as yC}from"node:util";import{fileURLToPath as bC}from"node:url";var kC=yC(gC),wk=Ai.dirname(bC(import.meta.url)),xC=Ai.resolve(wk,"..","..","client","dist"),SC="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",vC=new Map([...SC].map((t,e)=>[t,e]));function TC(t){let e=[],n=0,r=0;for(let o of t){let s=vC.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=TC(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 EC(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 hk=new Map;async function _C(t){let e;try{e=await mk.stat(t)}catch{return null}let n=hk.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await mk.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:AC(r.mappings||"")};return hk.set(t,o),o}catch{return null}}var yd=null;function IC(){return yd||(yd=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):kC("git",["rev-parse","--short","HEAD"],{cwd:Ai.resolve(wk,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),yd}var RC=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function $C(t,{distDir:e=xC}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(RC)){let[a,i,c,l]=s,u=await _C(Ai.join(e,"assets",`${i}.map`)),f=u?EC(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 gk(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await IC();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await $C(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var OC=()=>process.env.BOSTRAT_STATE_DIR||no.join(process.env.HOME||kk.tmpdir(),".bostrat"),An=()=>no.join(OC(),"telemetry"),xk=()=>no.join(An(),".relay-offsets.json"),CC=50,MC=14,NC=3e4,yk=500,Gt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function PC(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 Sk=t=>new Date(t).toISOString().slice(0,10),bk=0;async function vk(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,CC):[]).map(s=>PC(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await gk(s);await Ke.mkdir(An(),{recursive:!0});let o=no.join(An(),`${Sk(e)}.ndjson`);return await Ke.appendFile(o,r.map(s=>JSON.stringify(s)+`
412
+ `).join(""),"utf8"),e-bk>6*3600*1e3&&(bk=e,BC(e).catch(()=>{})),r.length}async function BC(t=Date.now()){let e=Sk(t-MC*24*3600*1e3),n=[];try{n=await Ke.readdir(An())}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(An(),s),{force:!0});let o=await Ak();for(let s of r)delete o[s];await Ek(o)}var Ei=new Map;function Tk(t,e=Date.now()){let n=t||"unknown",r=Ei.get(n);return r||(Ei.size>512&&Ei.clear(),r={tokens:40,at:e},Ei.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(xk(),"utf8"))||{}}catch{return{}}}async function Ek(t){await Ke.mkdir(An(),{recursive:!0}),await Ke.writeFile(xk(),JSON.stringify(t),"utf8")}function DC(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||kk.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 jC({fetchImpl:t=fetch}={}){let e=await Cm();if(!e)return{sent:0,disabled:!0};let n;try{n=(await Ke.readdir(An())).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(An(),s),i=r[s]||0,c;try{let d=await Ke.open(a,"r");try{let p=(await d.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
413
+ `).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=yk){let p=f.slice(d,d+yk).map(g=>DC(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 Ek(r)}return{sent:o,disabled:!1}}var bd=null,kd=!1;function _k(){bd||(bd=setInterval(async()=>{if(!kd){kd=!0;try{await jC()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{kd=!1}}},NC),bd.unref?.())}import Ii from"node:fs/promises";import Ck from"node:path";import LC from"node:os";var Mk=()=>process.env.BOSTRAT_STATE_DIR||Ck.join(process.env.HOME||LC.tmpdir(),".bostrat"),Nk=()=>Ck.join(Mk(),"dictation-glossary.json"),FC=100,UC=500,HC=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Ri=null,xd=null,Sd=Promise.resolve();function Ik(){let t={},e=Date.now();for(let n of HC)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function Pk(){return Ri||(xd??=Ii.readFile(Nk(),"utf8").then(t=>JSON.parse(t)?.terms??Ik()).catch(()=>Ik()).then(t=>Ri=t).finally(()=>xd=null),xd)}async function Rk(){let t=JSON.stringify({terms:Ri},null,2),e=Nk(),n=`${e}.${process.pid}.tmp`;await Ii.mkdir(Mk(),{recursive:!0}),await Ii.writeFile(n,t),await Ii.rename(n,e)}function WC(){return Sd=Sd.then(Rk,Rk),Sd.catch(()=>{})}async function vd(){return Object.values(await Pk()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var qC=t=>Math.max(1,Math.ceil(t.length/4));async function KC(){let t=[],e=0;for(let{term:n}of await vd()){let r=qC(n);if(!(e+r>UC)&&(t.push(n),e+=r,t.length>=FC))break}return t}async function GC(t,e){let n=await Pk(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var $k=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),_i=t=>t.toLowerCase();function Ok(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Bk(t,e){let n=$k(t),r=$k(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]=_i(n[u])===_i(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;)_i(n[c])===_i(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(Ok(r[l])&&i.add(r[l]),l++);for(;l<s;l++)Ok(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await GC(u,"learned");return await WC(),[...i]}var xs=null;function VC(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 Dk(){if(!xs)return null;let t=await Oo();if(!t||t.apiKey.slice(-6)!==xs.keyTail)return null;let{keyTail:e,...n}=xs;return n}async function jk(t,e){let n=await Oo();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 KC())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=VC(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 Td={"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 zC(t){if(!t)return null;let e=String(t).trim();if(Td[e])return e;let n=null;for(let r of Object.keys(Td))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function JC(t,e={}){let n=Td[zC(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 XC(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 YC(){if(!Ss)return null;let t=await ka();if(!t||t.apiKey.slice(-6)!==Ss.keyTail)return null;let{keyTail:e,...n}=Ss;return n}async function ZC(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=XC(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 QC={openai:ZC};async function Lk(){let t=await ka();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await YC()}:{configured:!1}}async function Fk(){let t=await ka();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 QC[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function Uk(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=JC(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 Vk=lt.dirname(eM(import.meta.url)),Hk=Number(process.env.PORT)||3362,$i=mb(process.env),zk=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,ro=Kb(process.env.BOSTRAT_ALLOW_IPS),Rd=Gb(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Si),Jk=zb(),Xk=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=Se();x.disable("x-powered-by");x.use((t,e,n)=>{if(ws(t)||ro.allowed(ro.clientIp(t))||Rd.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(tk);x.use(Yb({token:Jk,enforce:Xk}));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 Ye(),autoTriggerPct:zk})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await Rn()})});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(!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 bo(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});x.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!wt(n))return e.status(400).json({error:"invalid session name"});try{let r=await 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=oa(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 Wk=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>Wk){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,Wk).map(a=>a.id));o=n.filter(a=>!a.archivedAt||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:oi,agentAvailability:await Bu(),tickets:await Sa()})});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 xb(n);if(r.cursorUsage=du(),t.query.format==="text")return e.type("text/plain").send(Sb(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 $b(await ot(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=du(),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 Fb(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 bh(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 kh(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 qy()})}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 Nd(t.get(_s),{byToken:ky,legacy:()=>by(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 fb(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(!Tk(ro.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await vk(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 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 Cs(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:Qw(),latency:Ag()})});x.post("/api/github/refresh",(t,e)=>{qd(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await Hb(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 Wb(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function $d(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 Oi({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 ot()).find(s=>s.workspace?.branch===n&&$d(s,t,r))??null:null}async function Yk({repo:t=null,number:e=null,branch:n=null}){let r=await Oi({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&&$d(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 Oi({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 sM(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 $t(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&$d(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 Cg();await sM(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(!Zo.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 Fe(n)).path);if(!b)throw new Error("repo has no GitHub remote");let S=await Oi({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 Qo(n,y),await Wt({repo:b,number:y,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Do(b,y,{merge:{method:g}}))throw new Error("couldn't record the auto-merge arm");return await Tr(b,y,i===!0),await Ra(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 Fr(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 Tr(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Ra(p,d.number,!0).catch(()=>{}),await wn(p,d.number).catch(()=>{}),m=await hn(p,d.number).catch(()=>!1),await cn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let g=await Oi({repo:n,number:d.number,branch:l}).catch(()=>null);g&&!g.archivedAt&&await Vr(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 Fe(n)).path).catch(()=>null),l=c?await jo(c,i).catch(()=>null):null;try{await yu(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 Bg(n,{method:r||"merge",onMerged:a=>{se("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&wn(o,a.number).catch(()=>{}).then(()=>hn(o,a.number)).catch(()=>{}).then(()=>cn(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:Pg()})});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(!Zo.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 Tr(d.repo,d.number,o===!0).catch(()=>{}),await Ra(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 Jn(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let g=await Lr(m,d.number);f.push({r:p,l:d,repoPath:m,pr:g,plan:Mg(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 Ng(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Fr(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 wn(p.repo,p.number).catch(()=>{}),await hn(p.repo,p.number).catch(()=>{}),await cn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await Qo(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Do(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 jo(a.repo,a.number).catch(()=>null),c=await Jn(a.repo).catch(()=>null);c&&await yu(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 zo(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 ku(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 ku(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 zo(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 Py(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 By(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 Lr(n,r),s=await tt(n).catch(()=>null),a=await Yk({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 Dg(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 Lt.promises.mkdir(f,{recursive:!0}),await Lt.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
410
414
  ${b.log??"(no fetchable log \u2014 see link)"}
411
415
  `).join(`
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}
416
+ `));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 ih(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 Lr(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 Yk({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 ch(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=Fw.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,La(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&&Ay(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:nk(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await pa({trackId:t.query.track||void 0});e.json({views:n.map(vs(t)),maxLive:rn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await um(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:vs(t)(n),session:br(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 Dc({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 dm(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 fm(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 pm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var aM=[["linear",Dn,qc],["jira",ba,Hc]],Ad=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await Uc(),r={};for(let[o,s,a]of aM){let i=await s();if(!i){Ad.delete(o),r[o]={configured:!1};continue}let c=Ad.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},Ad.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 Uc()})}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 Om(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 R({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await Q(n):null;if(!s&&r&&(s=(await ot()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+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 vr(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 Gm(),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 vr(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 nl(t.params.id)})});var Mi=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 Xm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Mi(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await rl(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Mi(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Ym(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Mi(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 Zm(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Mi(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 th(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 Qm()})}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 eh(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 Nn({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 nn(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 nn(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(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 nn(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(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 ot()).find(p=>p.cwd&&(a===p.cwd||a.startsWith(p.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 qp({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 Kp(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 Vp(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 Zg(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await zp(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 Vg({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 zg(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 Jg({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 Xg(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 Yg(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 wa({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 Lc(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 Lc(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=ym(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(Me(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 xm({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 Sm(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 vm(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 Tm(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=sk(t.query.since||null);e.json({since:n,repos:hd(),prs:await ak(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 ik(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,Ed=400,Ci=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>Ed)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Ed} cap; a notification is a nudge, put the detail in chat`});let c=Ro(`${n||""}
417
+ ${i}`);if(c.length)return e.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await Q(a):null;if(!l&&s&&(l=(await ot()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+lt.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,iM):l.name,body:f(i,Ed),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:Ci?Ci.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 jk(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 Oo(),terms:await vd(),problem:await Dk()})});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 Bk(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 Lk())});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 Fk())}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 Uk(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 Hu({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await vr(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:Zk(us(t.params.id)),subtasks:zr(t.params.id).map(Ts)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Hy(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await nl(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 Vr(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 Gy(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 Du(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}
414
418
  data: ${JSON.stringify(i)}
415
419
 
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
420
+ `),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=cp(n.id,r),a=setInterval(()=>e.write(`: hb
417
421
 
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
422
+ `),25e3);t.on("close",()=>{clearInterval(a),s()})});x.get("/api/tracks/:id/search",async(t,e)=>{let n=await Q(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=String(t.query.q||"").trim();if(!r)return e.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(t.query.k)||8,1),50),s=So(await D(n)),a=Zs(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 of()})});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 sf(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 cM=["Run a memory consolidation pass on this workspace's `.bostrat/memory/` directory:","1. Read `MEMORY.md` and every fact file.","2. Merge duplicates and facts that describe the same thing (keep the best-written one; fold unique details in; give the survivor a `supersedes:` entry naming what it absorbed, delete the absorbed file, and remove its index line).","3. Resolve contradictions in favor of the newest evidence \u2014 check the code where feasible before deciding.","4. Mark clearly-stale facts: correct them in place or delete them (file + index line).","5. Bump `metadata.modified` (ISO-8601, now) on every fact you touch.","6. Keep `MEMORY.md` one line per fact, under 200 lines.","When done, summarize what you merged, corrected, and deleted \u2014 that summary is the review record."].join(`
423
+ `);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await oe(t.params.id,cM)})}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 zy(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 oy(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||!Eu(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(Au(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 lM=/^[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(!lM.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=tM.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?fc(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 Fu(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:ii(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 ju(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 Fy({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 $y(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 bi(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await Oy(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 Qy(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:zr(t.params.id).map(Ts)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await xy(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(zu(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(eb(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 Ku(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 Ey(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(Zk(us(t.params.id))??{running:!1})});function Zk(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:Ls(),paths:Sf,trace:await xf({limit:n})})});var _d=lt.resolve(Vk,"..","..","client","dist");Lt.existsSync(_d)&&(x.use(Se.static(_d)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(lt.join(_d,"index.html"))));var Qk=nM.createServer(x),uM=new oM({noServer:!0}),tx=(t,e,n)=>{if(!(ws(t)||ro.allowed(ro.clientIp(t))||Rd.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
420
424
  \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
425
+ `),e.destroy();return}if(ek(t,e,n))return;if(!Zb(t,Jk,{enforce:Xk})){e.write(`HTTP/1.1 401 Unauthorized\r
422
426
  \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
427
+ `),e.destroy();return}let r=Mf(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
424
428
  \r
425
429
  `),e.destroy();return}let o=r.sessionName;if(!wt(o)){e.write(`HTTP/1.1 400 Bad Request\r
426
430
  \r
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)}
431
+ `),e.destroy();return}let{cols:s,rows:a}=r;uM.handleUpgrade(t,e,n,i=>$f(i,o,{cols:s,rows:a}))};Qk.on("upgrade",tx);var ex=process.env.BOSTRAT_CERT_DIR||lt.join(process.env.HOME||Id.tmpdir(),".bostrat","certs"),qk=Number(process.env.BOSTRAT_TLS_PORT)||3443,Kk=lt.join(ex,"bostrat.globopi.key"),Gk=lt.join(ex,"bostrat.globopi.crt");if(Lt.existsSync(Kk)&&Lt.existsSync(Gk)){let t=rM.createServer({key:Lt.readFileSync(Kk),cert:Lt.readFileSync(Gk)},x);t.on("upgrade",tx),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(qk,$i,()=>{console.log(`[bostrat-agent] https listening on ${$i||"0.0.0.0"}:${qk}`)})}Qk.listen(Hk,$i,()=>{console.log(`[bostrat-agent] listening on ${$i||"0.0.0.0"}:${Hk}`),console.log(`[bostrat-agent] tmux socket: ${Ut}`),console.log(`[bostrat-agent] auto-trigger default: ${zk}%`),console.log(`[bostrat-agent] ip allowlist: ${ro.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Rd.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Yy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Zy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Vy().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)),Ly().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)),mM(),pM(),Pb()&&Bb({listTracks:ot}),pk(),nh({resolveTrack:Q}),db({listTracks:ot,createTrack:Hu,archiveTrack:Vr,listRepos:ve,resolveTrack:Q,sayToLead:oe,answerPrompt:ju,startLeadHandoff:Ku,interruptLead:Fu,listSubtaskJobs:zr,cancelSubtask:zu,readEvents:D,appendEvent:R,knownAgents:oi}),dM(),go().then(t=>{t||console.warn(`[boot] no host tmux server on ${Ut} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),_k(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Ci=yb({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Id.hostname(),name:process.env.BOSTRAT_NODE_NAME||Id.hostname(),listTracks:ot,detectAgents:Bu}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),ir().catch(()=>{}),_m().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),rh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),Yp().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),ey().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),mh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{Ci?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function dM(){fM(),fa().catch(()=>{}),setInterval(()=>fa().catch(()=>{}),1e4)}function fM(){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(Vk,"..","bin",t),n=lt.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Lt.existsSync(e))continue;Lt.mkdirSync(n,{recursive:!0});let r=lt.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 pM(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await ot(),r=await Cb({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 mM(){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 Du(n.id).catch(()=>{}),await jg(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}