bostrat-node 1.6.3 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +157 -152
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await
|
|
1
|
+
var sT=Object.defineProperty;var M=(t,e)=>()=>(t&&(e=t(t=0)),e);var Rl=(t,e)=>{for(var n in e)sT(t,n,{get:e[n],enumerable:!0})};import{createHash as aT,randomBytes as iT,timingSafeEqual as cT}from"node:crypto";function Km(){return iT(32).toString("hex")}function ss(t){let e=typeof t=="string"?t.trim():"";return e?aT("sha256").update(e).digest("hex"):null}function Gm(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:cT(Buffer.from(t),Buffer.from(e))}function as(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if($l.set(t,($l.get(t)||0)+1),Ol.has(t)&&n-Ol.get(t)<lT)return!1;Ol.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=${$l.get(t)}${o?` ${o}`:""}`),!0}async function Vm(t,{byToken:e,legacy:n,env:r=process.env}={}){let o=typeof t=="string"?t.trim():"";if(!o)return r.BOSTRAT_HOOK_LEGACY_DENY==="1"?{mode:"legacy-denied",target:null}:{mode:"legacy",target:await n()};let s=await e(o);return s?.track?{mode:"token",target:s}:{mode:"refused",target:null}}var Ja,Ya,$l,Ol,lT,is=M(()=>{Ja="BOSTRAT_HOOK_TOKEN",Ya="x-bostrat-hook";$l=new Map,Ol=new Map,lT=6e4});import te from"node:fs/promises";import zm from"node:os";import Vt from"node:path";import{execFile as uT}from"node:child_process";import{promisify as dT}from"node:util";function Xa(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function fT(){return process.env.CLAUDE_CONFIG_DIR?Vt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Vt.join(zm.homedir(),".claude.json")}function pT(){return process.env.CLAUDE_CONFIG_DIR?Vt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Vt.join(zm.homedir(),".claude","settings.json")}async function hn(t){if(!t||typeof t!="string")return;let e=Vt.resolve(t),n=fT(),r=pT(),o={};try{o=JSON.parse(await te.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 te.mkdir(Vt.dirname(n),{recursive:!0}),await te.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await te.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,i.tui===void 0&&(i.tui="default"),process.env.BOSTRAT_HOOKS!=="off"){let l=`curl -sS -m 3 -X POST ${process.env.BOSTRAT_HOOK_URL||`http://127.0.0.1:${process.env.PORT||3362}/api/hook`} -H 'content-type: application/json' -H "${Ya}: $${Ja}" --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 te.mkdir(Vt.dirname(r),{recursive:!0}),await te.writeFile(r,JSON.stringify(i,null,2)),await mT(e)}async function mT(t){try{let e=Vt.resolve(t),n=Vt.join(e,".bostrat","memory","claude"),r=Vt.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await te.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Vt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await te.mkdir(n,{recursive:!0}),await te.mkdir(Vt.dirname(r),{recursive:!0}),await te.writeFile(r,JSON.stringify(o,null,2)),await hT(e,["/.claude/settings.local.json"])}catch{}}async function hT(t,e){try{let{stdout:n}=await dT(uT)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=Vt.resolve(t,n.trim()),o=await te.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await te.mkdir(Vt.dirname(r),{recursive:!0}),await te.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var cs=M(()=>{is()});import Cl from"node:fs/promises";import wT from"node:os";import Za from"node:path";function gT(){let t=process.env.CODEX_HOME||Za.join(wT.homedir(),".codex");return Za.join(t,"config.toml")}function Jm(t){return`[projects."${t}"]`}function yT(t,e){let n=Jm(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 Wr(t){if(!t||typeof t!="string")return;let e=Za.resolve(t),n=gT(),r="";try{r=await Cl.readFile(n,"utf8")}catch{}if(yT(r,e))return;let o=Jm(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
|
-
`;r=r.slice(0,a)+
|
|
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
|
|
14
|
-
`,"utf8")}var
|
|
13
|
+
`:"")}try{await Cl.mkdir(Za.dirname(n),{recursive:!0}),await Cl.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}var Qa=M(()=>{});import Ym from"node:fs/promises";import bT from"node:os";import ls from"node:path";function kT(t){let n=ls.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||ls.join(bT.homedir(),".cursor");return ls.join(r,"projects",n)}async function qr(t){if(!t||typeof t!="string")return;let e=ls.resolve(t),n=kT(e),r=ls.join(n,".workspace-trusted");await Ym.mkdir(n,{recursive:!0}),await Ym.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
|
|
14
|
+
`,"utf8")}var ti=M(()=>{});import Nl from"node:fs/promises";import wn from"node:path";import{execFile as xT}from"node:child_process";import{promisify as ST}from"node:util";async function IT(t){try{return(await Nl.stat(wn.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function RT(t){let e=i=>Nl.readFile(wn.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 He(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await IT(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:wn.basename(o),branch:await RT(o)});return}if(s>=ET)return;let i;try{i=await Nl.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||_T.has(c.name)||await e(wn.join(o,c.name),s+1)}for(let o of AT)await e(wn.resolve(o),0);let n=new Set,r=[];for(let o of t.values()){let s=(await lt(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 $T(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}async function lt(t){if(!t)return null;if(Ml.has(t))return Ml.get(t);let e=null;try{let{stdout:n}=await vT("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=$T(n)}catch{e=null}return Ml.set(t,e),e}async function Kr(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await He())if((await lt(n.path))?.toLowerCase()===e)return n.path;return null}var vT,Xm,TT,AT,ET,_T,Ml,sr=M(()=>{vT=ST(xT),Xm=process.env.HOME||"/home/globo",TT=wn.join(process.env.BOSTRAT_STATE_DIR||wn.join(Xm,".bostrat"),"repos"),AT=[...(process.env.BOSTRAT_REPOS_ROOT||wn.join(Xm,"Documents")).split(","),TT].map(t=>t.trim()).filter(Boolean),ET=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,_T=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Ml=new Map});import OT from"node:fs";import Zm from"node:fs/promises";import us from"node:path";import CT from"node:os";import{fileURLToPath as MT}from"node:url";import{execFile as NT}from"node:child_process";import{promisify as PT}from"node:util";function bt(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 ei(t=process.env){return!!(bt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function ni(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!th.test(n))throw Gr("repo must be owner/name",400);let r=Pl.get(n);if(r&&r.expiresAt-Date.now()>jT)return r;if(!ei(e))throw Gr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await eh(`${bt(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:nh(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Gr(`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 Gr(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 Pl.set(n,a),a}async function ds({env:t=process.env,force:e=!1}={}){if(!ei(t))return{configured:!1,repos:[],username:null};if(!e&&ar&&Date.now()-ar.at<LT)return ar.data;try{let n=await eh(`${bt(t)}/v1/github/repos`,{headers:nh(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return ar={at:Date.now(),data:r},r}catch(n){return ar?ar.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function rh(){Pl.clear(),ar=null}async function Ee({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await lt(t):null);if(!r)return{};let{token:o}=await ni(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Bl({env:t=process.env}={}){return(await ds({env:t})).username??null}function WT(t){let[e,n]=String(t).split("/");return us.join(DT,e,n)}async function Dl(t,{env:e=process.env}={}){let n=(...r)=>Qm("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${UT()}" ${HT(e)}`),await n("config","credential.useHttpPath","true")}async function ri(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!th.test(String(t||"")))throw Gr(`bad repo slug "${t}"`,400);let r=WT(t);if(OT.existsSync(us.join(r,".git")))return r;let{token:o}=await ni(t,{env:e});await Zm.mkdir(us.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Qm("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:FT,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Zm.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Gr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Dl(r,{env:e}),r}var Qm,BT,DT,th,jT,LT,FT,eh,Pl,ar,nh,Gr,UT,HT,zt=M(()=>{sr();Qm=PT(NT),BT=process.env.BOSTRAT_STATE_DIR||us.join(process.env.HOME||CT.tmpdir(),".bostrat"),DT=us.join(BT,"repos"),th=/^[^/\s]+\/[^/\s]+$/,jT=5*6e4,LT=6e4,FT=10*6e4,eh=fetch,Pl=new Map,ar=null;nh=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),Gr=(t,e)=>Object.assign(new Error(t),{status:e});UT=()=>MT(new URL("../bin/bostrat-git-credential",import.meta.url)),HT=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`});function oh(t=process.env){return!!(bt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function ah(t,e,n){let r=bt(n),o=new AbortController,s=setTimeout(()=>o.abort(),KT);try{return await GT(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function ih(t,{env:e=process.env,force:n=!1}={}){if(!oh(e))return{facts:[],source:"unconfigured"};let r=jl.get(t);if(!n&&r&&Date.now()-r.at<qT)return{facts:r.facts,source:"cache"};try{let o=await ah(`/v1/memory/${encodeURIComponent(t)}`,{headers:sh(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return jl.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 oi(t,e,{env:n=process.env}={}){if(!oh(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await ah(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...sh(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 jl.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 qT,KT,jl,GT,sh,ch=M(()=>{zt();qT=6e4,KT=5e3,jl=new Map,GT=(...t)=>fetch(...t);sh=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`})});import q from"node:fs/promises";import VT from"node:os";import rt from"node:path";import{execFile as zT}from"node:child_process";import{promisify as JT}from"node:util";function si(){return rt.join(Ll,"memory","repos")}function ir(t){return t===We?rt.join(Ll,"memory",We):rt.join(si(),t)}async function fs(t){try{return JSON.parse(await q.readFile(rt.join(t,ph),"utf8"))}catch{return{}}}async function Fl(t,e){let n=await fs(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await q.mkdir(t,{recursive:!0}),await q.writeFile(rt.join(t,ph),JSON.stringify(n))}function uh(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 dh(t){try{let{stdout:e}=await YT("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function Ul(t){if(!t)return[];let e=uh(await dh(t));if(e)return[e];let n=[],r=[];try{r=await q.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=uh(await dh(rt.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function ai(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 gn(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function _e(t,e=null){let n=e??await q.readFile(t,"utf8").catch(()=>""),{fm:r}=ai(n),o=gn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await q.stat(t)).mtimeMs}catch{return 0}}function mh(t,e){let n=gn(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===We){let o=gn(t,"trust");if(!o||/^operator/i.test(o))return We}return e}function Hl(t,e,n,r=new Map){let o=(t||"").split(`
|
|
15
15
|
`),s=new Map;for(let c of(e||"").split(`
|
|
16
|
-
`)){let l=
|
|
16
|
+
`)){let l=Vr(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=Vr(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
|
|
20
|
-
`),o);if(a.length){let
|
|
21
|
-
`).filter(f
|
|
22
|
-
`)}await
|
|
23
|
-
`);e.length>
|
|
24
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
25
|
-
`);return n}async function
|
|
19
|
+
`)}function hh(t,e){let n=gn(e,"name")||t.replace(/\.md$/,""),r=gn(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function ps(t){let e=[];try{e=await q.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 wh(t,e){let n=rt.join(t,".bostrat","memory"),r=await ps(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=rt.join(n,l),f;try{f=await q.readFile(u,"utf8")}catch{continue}let{fm:d}=ai(f),p=mh(d,e);if(!p)continue;let m=ir(p),w=rt.join(m,l),y=await _e(u,f);if(i.has(m)||i.set(m,await fs(m)),(i.get(m)[l]||0)>=y)continue;let k=await _e(w).catch(()=>0);if(await q.access(w).then(()=>!0).catch(()=>!1)&&k>=y)continue;await q.mkdir(m,{recursive:!0}),await q.writeFile(w,f),a++;let _=o.get(m);_||o.set(m,_={won:new Set,generated:new Map}),_.won.add(l),_.generated.set(l,hh(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await q.readFile(rt.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=rt.join(l,"MEMORY.md"),d=await q.readFile(f,"utf8").catch(()=>"");await q.writeFile(f,Hl(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function ZT(){let t=rt.join(Ll,"workspaces"),e=[];try{e=await q.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=rt.join(t,o.name);if(!await q.access(rt.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await Ul(s),c=await wh(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}}async function eA(t,e){let n=ir(t),r=await fs(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!QT.test(l)||l==="MEMORY.md")continue;let u=rt.join(n,l),f=await q.access(u).then(()=>!0).catch(()=>!1),d=f?await _e(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await q.rm(u,{force:!0}),await Fl(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await q.mkdir(n,{recursive:!0}),await q.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=rt.join(n,"MEMORY.md"),l=await q.readFile(c,"utf8").catch(()=>""),u=Hl(l,[...s.values()].join(`
|
|
20
|
+
`),o);if(a.length){let f=new Set(a);u=u.split(`
|
|
21
|
+
`).filter(d=>!f.has(Vr(d))).join(`
|
|
22
|
+
`)}await q.mkdir(n,{recursive:!0}),await q.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Wl(t,e=null){let n=ir(t),r=e?.length?e:await ps(n);if(!r.length)return{pushed:0,source:"empty"};let o=tA(await q.readFile(rt.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=rt.join(n,a),c;try{c=await q.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await _e(i,c),indexLine:o.get(a)??null})}return oi(t,s)}async function nA(t){let{facts:e,source:n}=await ih(t);e.length?await eA(t,e):n==="plane"&&await Wl(t)}function rA(t){let e=t.split(`
|
|
23
|
+
`);e.length>lh&&(e=e.slice(0,lh));let n=e.join(`
|
|
24
|
+
`);for(;Buffer.byteLength(n,"utf8")>XT&&e.length;)e.pop(),n=e.join(`
|
|
25
|
+
`);return n}async function sA(t,e){if(!t||!e?.length)return{materialized:!1,reason:"no-repo-key"};let n=rt.join(t,".bostrat","memory"),r=0,o=0,s=[];for(let u of e){let f=ir(u),d=await fs(f);for(let[y,k]of Object.entries(d)){let x=rt.join(n,y);await q.access(x).then(()=>!0).catch(()=>!1)&&await _e(x)<=k&&(await q.rm(x,{force:!0}),o++)}let p=await ps(f);if(!p.length)continue;await q.mkdir(n,{recursive:!0});for(let y of p){let k=rt.join(f,y),x=rt.join(n,y),_=await _e(k),O=await _e(x).catch(()=>0);await q.access(x).then(()=>!0).catch(()=>!1)&&O>=_||(await q.copyFile(k,x),r++)}let m=await q.readFile(rt.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===We?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${w}
|
|
26
26
|
|
|
27
27
|
${m.trim()}`:m.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
|
|
28
28
|
|
|
29
29
|
`)+`
|
|
30
|
-
`,c=
|
|
31
|
-
`).filter(p=>!s.has(
|
|
32
|
-
`);await
|
|
33
|
-
`).filter(l=>
|
|
34
|
-
`)).catch(()=>{});let c=await
|
|
35
|
-
`)){let r=
|
|
30
|
+
`,c=rA(i);await q.mkdir(n,{recursive:!0}),await q.writeFile(rt.join(n,"MEMORY.md"),c);let l=rt.join(n,"claude");return await q.mkdir(l,{recursive:!0}),await q.writeFile(rt.join(l,"MEMORY.md"),oA,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function gh(t){try{let e=await Ul(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,We];if(Date.now()-fh>aA){fh=Date.now(),await iA();let{changedByKey:r}=await ZT();for(let[o,s]of r)await Wl(o,[...s])}for(let r of n)await nA(r);return await sA(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function iA(){let t=0,e=[];try{e=await q.readdir(si(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=rt.join(si(),n.name),o=ir(We),s=new Set,a=new Map;for(let p of await ps(r)){let m=rt.join(r,p),w;try{w=await q.readFile(m,"utf8")}catch{continue}let{fm:y}=ai(w);if(mh(y,n.name)!==We)continue;let k=rt.join(o,p),x=await _e(k).catch(()=>0),_=await _e(m,w);await q.mkdir(o,{recursive:!0}),_>x&&await q.writeFile(k,w),await q.rm(m,{force:!0}),s.add(p),a.set(p,hh(p,y)),t++}if(!s.size)continue;let i=Date.now();await Fl(r,[...s].map(p=>[p,i])),await oi(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=rt.join(r,"MEMORY.md"),l=await q.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
31
|
+
`).filter(p=>!s.has(Vr(p))).join(`
|
|
32
|
+
`);await q.writeFile(c,u).catch(()=>{});let f=rt.join(o,"MEMORY.md"),d=await q.readFile(f,"utf8").catch(()=>"");await q.writeFile(f,Hl(d,l,s,a))}return{moved:t}}async function yh(){let t=[],e=[We];try{for(let n of await q.readdir(si(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=ir(n),o=await ps(r),s=await fs(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await q.readFile(rt.join(r,i),"utf8").catch(()=>""),{fm:l}=ai(c);a.push({file:i,name:gn(l,"name")||i.replace(/\.md$/,""),description:gn(l,"description")||"",scope:gn(l,"scope")||null,modified:await _e(rt.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function bh(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=ir(t),r=rt.join(n,e);if(!await q.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await q.rm(r,{force:!0}),await Fl(n,[[e,s]]);let a=rt.join(n,"MEMORY.md"),i=await q.readFile(a,"utf8").catch(()=>"");await q.writeFile(a,i.split(`
|
|
33
|
+
`).filter(l=>Vr(l)!==e).join(`
|
|
34
|
+
`)).catch(()=>{});let c=await oi(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function kh(t){try{let e=await Ul(t),{harvested:n,changedByKey:r}=await wh(t,e[0]||null);for(let[o,s]of r)await Wl(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}var YT,Ll,lh,XT,We,ph,Vr,QT,tA,oA,fh,aA,ii=M(()=>{ch();YT=JT(zT),Ll=process.env.BOSTRAT_STATE_DIR||rt.join(process.env.HOME||VT.tmpdir(),".bostrat"),lh=200,XT=25*1024;We="operator";ph=".tombstones.json";Vr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;QT=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,tA=t=>{let e=new Map;for(let n of(t||"").split(`
|
|
35
|
+
`)){let r=Vr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};oA=`# Track memory
|
|
36
36
|
|
|
37
37
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
38
38
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
39
39
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
40
|
-
`;
|
|
41
|
-
`,n!=null?{mode:n}:void 0),await
|
|
42
|
-
`,"utf8");let n=(await
|
|
43
|
-
`).filter(Boolean);n.length>
|
|
40
|
+
`;fh=0,aA=3e4});import ql from"node:fs/promises";import cA from"node:path";async function Jt(t,e,{mode:n}={}){await ql.mkdir(cA.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++lA}.tmp`;await ql.writeFile(r,JSON.stringify(e,null,2)+`
|
|
41
|
+
`,n!=null?{mode:n}:void 0),await ql.rename(r,t)}var lA,yn=M(()=>{lA=0});import Ah from"node:fs/promises";import Eh from"node:os";import _h from"node:path";function kt(t=process.env){return!!(bt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function ws(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),uA);try{return await wA(`${bt(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}async function Rh(){return li||(Kl||=Ah.readFile(Ih(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>li={ops:Array.isArray(t?.ops)?t.ops:[],parked:Array.isArray(t?.parked)?t.parked:[]}).finally(()=>Kl=null),Kl)}async function ui(){await Jt(Ih(),li).catch(()=>{})}function vh(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function Vl(t){let e=await Rh(),n=vh(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=>vh(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>xh&&(e.ops=e.ops.slice(-xh)),await ui()}function Th(){ci||(ci=setTimeout(()=>{ci=null,zr().catch(()=>{})},gA),ci.unref?.())}function Gl(t){let e=new Error(`plane ${t}`);return e.refused=!0,e}async function bA(t,e=process.env){if(t.op==="record"){let n=await ws(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...hs(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:hA()}]})},e);if(!n.ok)throw Gl(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 Ah.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await ws(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...hs(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw Gl(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 ws(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:hs(e)},e);if(!n.ok)throw Gl(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function zr({env:t=process.env,log:e=console,now:n=Date.now()}={}){return kt(t)?ms||(ms=(async()=>{let r=await Rh();kA(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await bA(a,t)}catch(c){if(!c?.refused)return Th(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<fA)return Th(),await ui(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};e.error?.(`[content-plane] ALARM parking ${a.op} ${a.family}/${a.id} after ${a.refusals} refusals (${c.message}) \u2014 draining the ${r.ops.length-1} ops behind it`),r.ops.shift(),r.parked.push({...a,parkedAt:n,error:c.message}),r.parked.length>Sh&&(r.parked=r.parked.slice(-Sh)),s++,await ui();continue}i.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await ui()}return{flushed:o,parked:s,pending:0}})().finally(()=>{ms=null}),ms):{flushed:0,source:"unconfigured"}}function kA(t,e){if(!t.parked.length||t.ops.length)return;let n=t.parked.filter(r=>e-(r.parkedAt||0)>=yA);if(n.length){t.parked=t.parked.filter(r=>!n.includes(r));for(let r of n)t.ops.push({...r,refusals:0,parkedAt:void 0,error:void 0})}}async function Ht(t,e,{trackId:n=null,env:r=process.env}={}){return kt(r)?(await Vl({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),zr({env:r})):{queued:!1,source:"unconfigured"}}async function bn(t,e,n,r,o,{env:s=process.env}={}){return kt(s)?(await Vl({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),zr({env:s})):{queued:!1,source:"unconfigured"}}async function he(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return kt(r)?(await Vl({op:"delete",family:t,id:e,deletedAt:n}),zr({env:r})):{queued:!1,source:"unconfigured"}}async function Ct(t,{env:e=process.env}={}){if(!kt(e))return{records:[],source:"unconfigured"};try{let n=await ws(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:hs(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 gs(t,e,n,r,{env:o=process.env}={}){if(!kt(o))return null;try{let s=await ws(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:hs(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="md"||r==="html"?a.toString("utf8"):a}catch{return null}}var uA,dA,xh,fA,Sh,pA,mA,Ih,hA,wA,gA,yA,hs,li,Kl,ci,ms,kn=M(()=>{zt();yn();uA=1e4,dA=3e4,xh=2e3,fA=5,Sh=100,pA=60*6e4,mA=()=>process.env.BOSTRAT_STATE_DIR||_h.join(process.env.HOME||Eh.tmpdir(),".bostrat"),Ih=()=>_h.join(mA(),"content-outbox.json"),hA=()=>process.env.BOSTRAT_NODE_ID||Eh.hostname(),wA=(...t)=>fetch(...t),gA=dA,yA=pA;hs=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});li=null,Kl=null,ci=null,ms=null});import zl from"node:fs/promises";import $h from"node:path";async function Oh(t,e,{box:n=null,agent:r=null}={}){if(!t||!kt())return{pushed:!1};try{let o=Date.now();return await Ht("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await bn("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function di(t){try{let e=vA(t);if(!e||!kt())return{restored:!1};let n=$h.join(t,xA,SA);if(await zl.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await Ct("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 gs("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await zl.mkdir($h.dirname(n),{recursive:!0}),await zl.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var xA,SA,vA,fi=M(()=>{kn();xA=".bostrat/handoff",SA="latest.md",vA=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null});import ys from"node:fs/promises";import TA from"node:os";import Mh from"node:path";function cr(t){let e=String(t??"");for(let n of AA)e=e.replace(n,"\u2039redacted\u203A");return e}function pi(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function EA(t){try{await ys.mkdir(Jl,{recursive:!0}),await ys.appendFile(Jr,`${JSON.stringify(t)}
|
|
42
|
+
`,"utf8");let n=(await ys.readFile(Jr,"utf8")).split(`
|
|
43
|
+
`).filter(Boolean);n.length>Ch&&await ys.writeFile(Jr,`${n.slice(-Ch).join(`
|
|
44
44
|
`)}
|
|
45
|
-
`,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${
|
|
46
|
-
`).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=
|
|
47
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,
|
|
48
|
-
`).map(n=>n.trim()).includes(t)}catch{return!1}}function
|
|
49
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
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
|
|
51
|
-
`).trim():""}function
|
|
52
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let w=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof w=="string"&&
|
|
53
|
-
|
|
54
|
-
`):""}var
|
|
55
|
-
`);if(!
|
|
56
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
57
|
-
`);return e}function
|
|
58
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
59
|
-
`).pop()||"";return
|
|
60
|
-
`),o=r.findIndex(i=>
|
|
61
|
-
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function
|
|
62
|
-
`),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
|
|
63
|
-
`))
|
|
64
|
-
`).slice(-
|
|
65
|
-
`);return xE.test(n)}function ws(t,e){return su(t,e)==="busy"}function fi(t,e){return su(t,e)!=="idle"}function pi(t,e){let n=Dt[t];return n?.up?n.up.test(e):!0}function vE(t,e){let n=Dt[t];return n?au(t,e)?!1:n.up.test(e)&&su(t,e)==="idle":!0}function au(t,e){let n=Dt[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="codex"?!0:!n.up.test(e):!1:!1}async function Hh(t,e,n){let r=Date.now()+n,o=0;for(await sr(t).catch(()=>{});;){let s=await yt(t);if(vE(e,s)){await vn(hs);return}if(e==="cursor"&&mE.test(s)&&!Dt.cursor.up.test(s)&&o<3){if(!Uh(t,"cursor-trust")){await vn(ru);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await Mt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await vn(hs);continue}if(e==="codex"&&o<3){let a=wE(s);if(a){if(!Uh(t,`codex-update:${a.from}->${a.to}`)){await vn(ru);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await Mt(t,"3",{enter:!1,kind:"codex-update-skip"}),Ph({sessionName:t,from:a.from,to:a.to}),await vn(hs);continue}}if(Date.now()>r){if(au(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Dt.codex.auth.test(s)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":e==="cursor"&&Dt.cursor.auth.test(s)?a="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":e==="claude"&&Dt.claude.auth.test(s)?a="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":e==="claude"&&Dt.claude.block.test(s)&&(a='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),G({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await Kr(t)){let a=`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw G({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),G({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Dt[e]?.block?.test(s),paneUp:!!Dt[e]?.up?.test(s),paneBusy:!!Dt[e]?.busy?.test(s)}).catch(()=>{}),await vn(hs);return}await vn(ru)}}function qh({agent:t,agentCmd:e}={}){let n=e?kn(e):kn(t||"");return{label:n,realClaude:e?rr(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 mi({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=qh({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=pE()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=qh({agent:r,agentCmd:o});if(i&&t&&await mn(t),c&&t&&await Br(t),l&&t&&await Dr(t),(i||c||l)&&n){await Hh(n,a,s);return}let u=Dt[r]?r:Dt[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Hh(n,u,s);return}await vn(hs)}var pE,ru,hs,Dt,mE,hE,gE,di,yE,bE,xE,SE,hi=M(()=>{Ht();Qo();Wa();qa();eu();ui();We();pE=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,ru=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,hs=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Dt={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key|Successfully logged in\.[\s\S]*Press Enter to continue\./},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/}},mE=/Trust this workspace|\[a\]\s*Trust/,hE=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;gE=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,di=new Map;yE=68,bE=/^\s*[─━]{10,}\s*$/;xE=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,SE=15});function Ie(t){return t==="codex"?{enter:!0,submitDelayMs:TE}:t==="claude"?{enter:!0,submitDelayMs:AE}:{enter:!0}}var TE,AE,gs=M(()=>{TE=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),AE=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})()});function ir(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of EE){let o=0;for(let s of _E[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Kh(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))IE.has(r)&&(e+=o||0),RE.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function ys(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 EE,_E,IE,RE,iu=M(()=>{EE=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],_E={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/]};IE=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),RE=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function Jh(t){let e=ys(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||$E.has(r)||n.push(r);return n}function OE(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(zh(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(zh(n,"assistant",o))}}return e}function zh(t,e,n){let r=ir(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 bs(t){let e=OE(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=Jh(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,d]of l){let f=n.get(u);f||n.set(u,f=new Map),f.set(i,d)}});let s=e.length;return{chunks:e,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function wi(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(Jh(e))];if(!i.length)return[];let c=new Map;for(let u of i){let d=t.postings.get(u);if(!d)continue;let f=d.size,p=Math.log(1+(t.N-f+.5)/(f+.5));for(let[m,w]of d){let y=t.lengths[m],k=w+Gh*(1-Vh+Vh*(y/(t.avgdl||1))),S=p*(w*(Gh+1)/k);c.set(m,(c.get(m)||0)+S)}}let l=[];for(let[u,d]of c){let f=t.chunks[u];o&&f.role!==o||s&&!f.tags.includes(s)||a&&f.boxId!==a||l.push({chunk:f,score:d})}return l.sort((u,d)=>d.score-u.score||d.chunk.seq-u.chunk.seq),l.slice(0,r)}var Gh,Vh,$E,cu=M(()=>{iu();Gh=1.5,Vh=.75,$E=new Set("a an and are as at be been but by for from had has have he her his i if in into is it its of on or our so than that the their them then there these they this those to was were will with would you your we".split(" "))});import gi from"node:fs/promises";import Yh from"node:path";async function CE(t){let e=Yh.join(t,".bostrat","events"),n;try{n=await gi.readdir(e)}catch{return bs([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await gi.readFile(Yh.join(e,o),"utf8");for(let a of s.split(`
|
|
66
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
67
|
-
${o}`.trim()||e.slice(0,2e3)}function
|
|
68
|
-
`):""}async function
|
|
45
|
+
`,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${Jr}: ${e?.message||e}`)}}async function G(t={}){let e={at:new Date().toISOString(),ts:Date.now(),...t},n=[e.flow||"handoff",e.phase||e.step||"?",e.trackId?`track=${e.trackId}`:"",e.sessionName?`box=${e.sessionName}`:"",e.error?`ERR=${e.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),e.sessionName&&pi())try{e.paneTail=cr((await xt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(pi()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await EA(e)}async function Nh({limit:t=80}={}){try{return(await ys.readFile(Jr,"utf8")).split(`
|
|
46
|
+
`).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=cr(o.paneTail)),o}catch{return{raw:cr(r)}}})}catch{return[]}}var AA,Jl,Jr,Ch,Ph,qe=M(()=>{Wt();AA=[/[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];Jl=process.env.BOSTRAT_DEBUG_DIR||Mh.join(TA.homedir(),".bostrat","debug"),Jr=Mh.join(Jl,"handoff.jsonl"),Ch=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;Ph={DEBUG_DIR:Jl,LOG_FILE:Jr}});import{execFile as _A}from"node:child_process";import{promisify as IA}from"node:util";import hi from"node:fs";import{randomUUID as RA}from"node:crypto";function Zr(t){return new Promise(e=>setTimeout(e,t))}function mi(t,e){if(!e)return t;let n=o=>typeof o=="string"?o.split(e).join("<redacted>"):o,r=new Error(n(t?.message)||"command failed");return t?.code!==void 0&&(r.code=t.code),t?.stderr!==void 0&&(r.stderr=n(t.stderr)),r}function Dh(t){return`/tmp/tmux-${t}/default`}function $A({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=Dh(r);return e?o:n?`${n}/bostrat/tmux-default`:o}function Zl(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${CA}=1`]}function tu(t,e,n){let r=lr(t),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);if(!r||o)return t;let s=`${t} --session-id ${e}`,a=/(^|\s)(--name|-n)(\s|=)/.test(t);return n&&!a&&St(n)&&(s+=` --name ${n}`),s}function lr(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function xn(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function NA(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:lr(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function jh(t,{loginShell:e=!1,failOpen:n=!1}={}){let r=await Lh(t,{loginShell:e});return r===wi?!0:r===Yr?!1:n}async function Lh(t,{loginShell:e=!1,timeoutMs:n=Yl}={}){if(!t)return Yr;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await ft(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:n}),wi}catch(s){return s?.killed||s?.signal?Xr:s?.code===1||s?.code===127?Yr:Xr}}async function gi(t,{retries:e=PA,sleep:n=BA,probe:r=Lh}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Yl});for(let a=0;s===Xr&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:Yl*2});return s}function F(...t){return["-S",ee,...t]}async function Sn(){if(!hi.existsSync(ee))return!1;try{return await ft(L,F("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function bs(){if(await Sn())return!0;try{hi.mkdirSync(ee.replace(/\/[^/]+$/,""),{recursive:!0}),await ft(L,F("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Sn()}function St(t){return typeof t=="string"&&DA.test(t)}async function we(){if(!hi.existsSync(ee))return[];try{let t="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}|#{alternate_on}",{stdout:e}=await ft(L,F("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,p]=r.split("|"),m=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(m)?n-m*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:p==="1"}}).filter(r=>!MA.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function ot(t){return(await we()).find(n=>n.name===t)||null}function FA(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=LA&&jA.test(e)}function yi(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Fh(t){try{let{stdout:e}=await ft(L,F("list-sessions","-F","#{session_name}"));return e.split(`
|
|
48
|
+
`).map(n=>n.trim()).includes(t)}catch{return!1}}function qA(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 KA(t,e){let n=Date.now()+UA(),r=qA(e),o=!1,s=0;for(;;){if(await Zr(HA),s+=1,!await Fh(t))return;let a=await xt(t,{lines:15}),i=!o&&WA.test(a)&&!!r?.test(a);if(!i&&s>1&&!await Qr(t))return;if(i){o=!0,await G({flow:"spawn",phase:"launch-retry",sessionName:t,agent:xn(e),promptHead:e.slice(0,120)}).catch(()=>{}),await ft(L,F("send-keys","-t",t,"C-u")),await ft(L,F("send-keys","-t",t,"-l",e)),await ft(L,F("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await G({flow:"spawn",phase:"launch-failed",sessionName:t,agent:xn(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function vn(t,e=OA,n={}){if(!FA(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=yi(r);if(!St(o))throw new Error("resulting tmux session name is invalid");if(!await bs())throw new Error(`host tmux server not running on ${ee} and auto-start failed \u2014 start one: ${L} -S ${ee} new-session -d -s keeper "sleep 2147483647"`);if(await Fh(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Xl,a=n.agent||xn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||RA(),l=i?s:tu(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&lr(s),d=n.firstRun===!0;d||(f&&await hn(e),a==="codex"&&await Wr(e),a==="cursor"&&await qr(e),await gh(e),await di(e));let p=f&&!d?Xa():null,m=Km(),w=["-e",`${Ja}=${m}`];p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&w.push(...Zl());try{await ft(L,F("new-session","-d","-s",o,"-c",e,...w))}catch(y){throw mi(p?mi(y,p):y,m)}return await ur(o),await ft(L,F("set-option","-t",o,"@bostrat-session","1")),await ft(L,F("set-option","-t",o,"@bostrat-title",r)),await ft(L,F("set-option","-t",o,"@bostrat-sid",c)),await ft(L,F("set-option","-t",o,"@bostrat-agent",a)),await ft(L,F("send-keys","-t",o,l,"Enter")),NA(l)&&KA(o,l).catch(()=>{}),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function Ie(t){if(!St(t))throw new Error("invalid session name");await ft(L,F("kill-session","-t",t))}async function Uh(t,e){if(St(t))try{let n=e?F("set-option","-t",t,"@bostrat-sid",e):F("set-option","-u","-t",t,"@bostrat-sid");await ft(L,n)}catch{}}async function xt(t,{lines:e=40}={}){if(!St(t))return"";try{let n=F("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await ft(L,n);return String(r||"")}catch{return""}}async function Hh(t){if(!St(t))return 0;try{let{stdout:e}=await ft(L,F("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
|
|
49
|
+
`).filter(Boolean).length}catch{return 0}}async function ur(t,{cols:e=zA,rows:n=JA}={}){if(!St(t))return!0;if(await Hh(t))return!1;try{let r=await Wh(t);if(r.cols===e&&r.rows===n)return!0;await ft(L,F("resize-window","-t",t,"-x",String(e),"-y",String(n))),await ft(L,F("set-window-option","-t",t,"window-size","latest")),await Zr(80)}catch{}return!0}async function YA(t){if(!St(t))return 0;try{let{stdout:e}=await ft(L,F("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function Qr(t){let e=await YA(t);if(!e)return!1;try{return await ft("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function Wh(t){if(!St(t))return{cols:0,rows:0};try{let{stdout:e}=await ft(L,F("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 bi(t,{cols:e=GA,rows:n=VA}={}){if(St(t)&&!await Hh(t))try{let r=await Wh(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await ft(L,F("resize-window","-t",t,"-x",String(o),"-y",String(s))),await ft(L,F("set-window-option","-t",t,"window-size","latest")),await Zr(80))}catch{}}async function Tn(t){return await bi(t),xt(t,{lines:Mt})}async function At(t,e,{settleMs:n=40,kind:r}={}){if(!St(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>XA.has(s));for(let s=0;s<o.length;s++)await ft(L,F("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await Zr(n);return G({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function to(t,e,{kind:n}={}){if(!St(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await ft(L,F("send-keys","-t",t,"-N",String(r),"BSpace")),G({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function xi(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!St(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await ft(L,F("send-keys","-t",t,"-l",s)),n>0&&await Zr(n);G({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function Nt(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!St(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await ft(L,F("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await Zr(s),await ft(L,F("send-keys","-t",t,"Enter"))}G({flow:"tmux",phase:"send",sessionName:t,kind:o||"text",textLen:typeof e=="string"?e.length:0,textHead:typeof e=="string"?e.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}var ft,L,Bh,ee,OA,Xl,CA,Ql,MA,wi,Yr,Xr,Yl,PA,BA,eu,DA,jA,LA,UA,HA,WA,GA,VA,Mt,zA,JA,XA,ki,Wt=M(()=>{cs();Qa();ti();ii();fi();qe();is();ft=IA(_A);L=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";Bh=typeof process.getuid=="function"?process.getuid():0,ee=$A({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return hi.existsSync(Dh(Bh))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Bh}),OA=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Xl=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",CA="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";Ql="bostrat-usage-probe",MA=new Set([Ql]);wi="present",Yr="absent",Xr="unknown",Yl=5e3,PA=[250,1e3,3e3],BA=t=>new Promise(e=>setTimeout(e,t));eu={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"};DA=/^[A-Za-z0-9_.-]{1,64}$/;jA=/^[A-Za-z0-9 ._↻-]+$/,LA=64;UA=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,HA=1e3,WA=/not found|Unknown command/i;GA=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,VA=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Mt=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,zA=Number(process.env.BOSTRAT_BOX_COLS)||120,JA=Number(process.env.BOSTRAT_BOX_ROWS)||40;XA=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);ki=1600});import ks from"node:fs";import rE from"node:fs/promises";import eo from"node:path";import oE from"node:os";function ou(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function su(t){let e=eo.join(ru,ou(t)),n;try{n=ks.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=eo.join(e,o);try{let{mtimeMs:a}=ks.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Ke(t,e){if(!e)return null;let n=eo.join(ru,ou(t),`${e}.jsonl`);return ks.existsSync(n)?n:null}function Yh(t){if(!t)return[];let e=eo.join(ru,ou(t)),n;try{n=ks.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=ks.statSync(eo.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Yt(t,e=131072){let n=await rE.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 aE(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 xs(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=Ke(t,e),o=!0,!r)return null}else if(n){if(r=su(t),o=!1,!r)return null}else return null;let s;try{s=aE(await Yt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Jh,s.model),i=Jh[s.model]||sE;return{source:"transcript",exact:o,model:s.model,windowKnown:a,window:i,occupancy:s.occupancy,pct:Math.min(1,s.occupancy/i),transcript:r}}var Jh,sE,ru,ge=M(()=>{Jh={"claude-opus-5":1e6,"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-5":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},sE=2e5,ru=process.env.BOSTRAT_CLAUDE_PROJECTS||eo.join(oE.homedir(),".claude","projects")});function iE(t,e){return!t||!e?null:Ke(t,e)}function cE(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 lE(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 uE(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function au(t){let e=[],n=t?.message?.content;if(!Array.isArray(n))return e;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)e.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&e.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return e}function Xh(t,e){if(!t.uuid)return null;let n=au(t).map((r,o)=>({uuid:t.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&e.get(r.toolUseId)||null}));return n.length?n:null}async function iu(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Yt(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
52
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let w=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof w=="string"&&dE.test(w)&&s.set(m.id,w.split("/").pop())}if(uE(l.content)){let m=Xh(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=cE(l.content),d=u==="assistant"?lE(l.content):null,p=u==="user"?Xh(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)}async function Zh(t,e,{turns:n=16}={}){let r=iE(t,e);if(!r)return"";let o=await iu(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}:** ${fE(s.text||c,700)} ${i}`.trim()}).join(`
|
|
53
|
+
|
|
54
|
+
`):""}var dE,fE,vi=M(()=>{ge();dE=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;fE=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t});import pE from"node:fs/promises";import{execFile as mE}from"node:child_process";import{promisify as hE}from"node:util";function nw(t){lu=typeof t=="function"?t:null}function gE(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function rw({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||Qh.has(r)||(Qh.add(r),tw=tw.then(()=>yE({sessionName:t,from:e,to:r})).catch(()=>{}))}async function yE({sessionName:t,from:e,to:n}){await G({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await cu("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await pE.realpath(r),s=gE(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await cu("npm",["install","-g","@openai/codex"],{env:a,timeout:wE});let i=(await cu(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await G({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await ew({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await G({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await ew({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function ew(t){if(lu)try{await lu(t)}catch{}}var cu,wE,Qh,tw,lu,uu=M(()=>{qe();cu=hE(mE),wE=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Qh=new Set,tw=Promise.resolve(),lu=null});function kE(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
|
|
55
|
+
`);if(!ow.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(bE);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"&&ow.test(a)?a:"The agent is asking for approval";return{provider:e,box:n,promptId:`${e}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}function dr(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
|
|
56
|
+
`);for(let r=n.length-1;r>=0;r--)if(iw.test(n[r]))return n.slice(r).join(`
|
|
57
|
+
`);return e}function xE(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 SE(t){return/device code/i.test(t)?"works from the device you're reading this on":/sign in with chatgpt/i.test(t)?"opens a browser link on the node itself \u2014 can't finish from another device":""}function vE(t,e=""){let n=dr(t);if(Ss.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(sw);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(sw.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&&!iw.test(r[l])&&xE(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=SE(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function EE(t,e=""){let n=dr(t);if(Ss.test(n)||!TE.test(n))return null;let r=n.trimEnd().split(`
|
|
59
|
+
`).pop()||"";return AE.test(r)?{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"codex:signin-complete",questions:[{question:"Codex successfully signed in and is waiting to open the session.",header:"Signed in",multiSelect:!1,options:[{label:"Continue",description:""}]}]}:null}function IE(t,e=""){let n=dr(t);if(Ss.test(n)||!aw.test(n))return null;let r=n.split(`
|
|
60
|
+
`),o=r.findIndex(i=>aw.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(_E);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 RE(t,e=""){let n=dr(t);return Ss.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 $E(t,e=""){let n=dr(t);return Ss.test(n)||!/Paste or type your API key/.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-apikey",questions:[{question:"Codex is asking for an OpenAI API key. For safety a key can only be typed in the Terminal tab, never through chat. To use your ChatGPT subscription instead, go back and pick a sign-in option.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function OE(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||du.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
61
|
+
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function ME(t,e=""){let n=String(t);if(!CE.test(n)||du.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
62
|
+
`),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 NE(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||du.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 cw(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=EE(e,n)||IE(e,n)||RE(e,n)||$E(e,n)||vE(e,n);if(r)return r}if(t==="cursor"){let r=ME(e,n)||NE(e,n)||OE(e,n);if(r)return r}return kE(e,{provider:t,box:n})}function lw(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 bE,ow,Ss,iw,sw,TE,AE,aw,_E,du,CE,Ti=M(()=>{bE=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,ow=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;Ss=/(^|\n)\s*›/,iw=/^\s*Welcome to Codex\b/;sw=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;TE=/Successfully logged in\./,AE=/^\s*Press Enter to continue\.\s*$/;aw=/Enter this one-time code/,_E=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;du=/Run Everything|Add a follow-up|Plan, search, build anything/;CE=/click this link to log in|Signing in with the browser/});function jE(t){let e=String(t||"");if(!Dt.codex.block.test(e))return null;let n=dr(e);if(!Dt.codex.block.test(n))return null;let r=n.match(DE);return{from:r?.[1]||null,to:r?.[2]||null}}function An(t){return new Promise(e=>setTimeout(e,t))}function uw(t,e){let n=Date.now(),r=LE();for(let[s,a]of Ai)n-a>=r&&Ai.delete(s);let o=`${t}::${e}`;return Ai.has(o)?!1:(Ai.set(o,n),!0)}function HE(t){let e=0;for(let n of String(t).split(`
|
|
63
|
+
`))UE.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function pu(t,e){let n=Dt[t];if(!n?.busy)return{state:"idle",reason:null};if(hu(t,e))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(e))return{state:"idle",reason:null};if(n.busy.test(e))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(e))return{state:"busy",reason:null};let r=HE(e);return t==="claude"&&r>0&&r<FE?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function mu(t,e){return pu(t,e).state}function fw(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
|
|
64
|
+
`).slice(-qE).join(`
|
|
65
|
+
`);return WE.test(n)}function Ts(t,e){return mu(t,e)==="busy"}function Ei(t,e){return mu(t,e)!=="idle"}function _i(t,e){let n=Dt[t];return n?.up?n.up.test(e):!0}function KE(t,e){let n=Dt[t];return n?hu(t,e)?!1:n.up.test(e)&&mu(t,e)==="idle":!0}function hu(t,e){let n=Dt[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="codex"?!0:!n.up.test(e):!1:!1}async function dw(t,e,n){let r=Date.now()+n,o=0;for(await ur(t).catch(()=>{});;){let s=await xt(t);if(KE(e,s)){await An(vs);return}if(e==="cursor"&&BE.test(s)&&!Dt.cursor.up.test(s)&&o<3){if(!uw(t,"cursor-trust")){await An(fu);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await Nt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await An(vs);continue}if(e==="codex"&&o<3){let a=jE(s);if(a){if(!uw(t,`codex-update:${a.from}->${a.to}`)){await An(fu);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await Nt(t,"3",{enter:!1,kind:"codex-update-skip"}),rw({sessionName:t,from:a.from,to:a.to}),await An(vs);continue}}if(Date.now()>r){if(hu(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Dt.codex.auth.test(s)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":e==="cursor"&&Dt.cursor.auth.test(s)?a="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":e==="claude"&&Dt.claude.auth.test(s)?a="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":e==="claude"&&Dt.claude.block.test(s)&&(a='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),G({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await Qr(t)){let a=`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw G({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),G({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Dt[e]?.block?.test(s),paneUp:!!Dt[e]?.up?.test(s),paneBusy:!!Dt[e]?.busy?.test(s)}).catch(()=>{}),await An(vs);return}await An(fu)}}function pw({agent:t,agentCmd:e}={}){let n=e?xn(e):xn(t||"");return{label:n,realClaude:e?lr(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 As({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=pw({agent:n,agentCmd:r});return!e||!o&&!s?null:En({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function En({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=PE()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=pw({agent:r,agentCmd:o});if(i&&t&&await hn(t),c&&t&&await Wr(t),l&&t&&await qr(t),(i||c||l)&&n){await dw(n,a,s);return}let u=Dt[r]?r:Dt[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await dw(n,u,s);return}await An(vs)}var PE,fu,vs,Dt,BE,DE,LE,Ai,FE,UE,WE,qE,Ii=M(()=>{Wt();cs();Qa();ti();uu();Ti();qe();PE=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,fu=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,vs=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Dt={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key|Successfully logged in\.[\s\S]*Press Enter to continue\./},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/}},BE=/Trust this workspace|\[a\]\s*Trust/,DE=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;LE=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Ai=new Map;FE=68,UE=/^\s*[─━]{10,}\s*$/;WE=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,qE=15});function Re(t){return t==="codex"?{enter:!0,submitDelayMs:GE}:t==="claude"?{enter:!0,submitDelayMs:VE}:{enter:!0}}var GE,VE,Es=M(()=>{GE=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),VE=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})()});function fr(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of zE){let o=0;for(let s of JE[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function mw(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))YE.has(r)&&(e+=o||0),XE.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function _s(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 zE,JE,YE,XE,wu=M(()=>{zE=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],JE={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/]};YE=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),XE=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function yw(t){let e=_s(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||ZE.has(r)||n.push(r);return n}function QE(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(gw(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(gw(n,"assistant",o))}}return e}function gw(t,e,n){let r=fr(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 Is(t){let e=QE(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=yw(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 Ri(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(yw(e))];if(!i.length)return[];let c=new Map;for(let u of i){let f=t.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(t.N-d+.5)/(d+.5));for(let[m,w]of f){let y=t.lengths[m],k=w+hw*(1-ww+ww*(y/(t.avgdl||1))),x=p*(w*(hw+1)/k);c.set(m,(c.get(m)||0)+x)}}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 hw,ww,ZE,gu=M(()=>{wu();hw=1.5,ww=.75,ZE=new Set("a an and are as at be been but by for from had has have he her his i if in into is it its of on or our so than that the their them then there these they this those to was were will with would you your we".split(" "))});import $i from"node:fs/promises";import bw from"node:path";async function t_(t){let e=bw.join(t,".bostrat","events"),n;try{n=await $i.readdir(e)}catch{return Is([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await $i.readFile(bw.join(e,o),"utf8");for(let a of s.split(`
|
|
66
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Is(r)}function e_(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}
|
|
67
|
+
${o}`.trim()||e.slice(0,2e3)}function n_(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Ri(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?["",`## ${kw}, 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(`
|
|
68
|
+
`):""}async function xw(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await $i.readFile(e,"utf8");if(a.includes(kw))return{appended:!1,reason:"already-enriched"};let i=await t_(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=n_(i,e_(a),{k:r,maxChars:o,exclude:s});return c?(await $i.appendFile(e,`
|
|
69
69
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
70
|
-
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var
|
|
71
|
-
`),
|
|
72
|
-
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function
|
|
73
|
-
`){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function
|
|
74
|
-
`,n);i!==-1&&(e=e.slice(0,i))}let r=
|
|
75
|
-
`).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function
|
|
76
|
-
`)){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(
|
|
77
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function
|
|
78
|
-
`)){let p=
|
|
79
|
-
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function
|
|
80
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let
|
|
81
|
-
`).trim():"";
|
|
82
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
83
|
-
`).map(e=>e.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(e=>e&&!/^#{1,6}[ \t]/.test(e)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function
|
|
70
|
+
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var kw,Sw=M(()=>{gu();kw="Relevant prior discussion (verbatim, retrieved"});import yu from"node:fs/promises";import no from"node:path";import r_ from"node:os";import{EventEmitter as o_}from"node:events";import{randomUUID as s_}from"node:crypto";function ro(t){return t?.cwd?no.join(t.cwd,".bostrat","events"):no.join(bu,"events")}async function l_(t,e){if(!Oi.has(e)){let o=(await U(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);Oi.set(e,o)}let n=Oi.get(e)+1;return Oi.set(e,n),n}function u_(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function $(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!a_.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!i_.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!c_.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?no.join(bu,"events"):ro(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=u_(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await l_(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 yu.mkdir(r,{recursive:!0}),await yu.appendFile(vw(r,n),JSON.stringify(i)+`
|
|
71
|
+
`),Xt.emit("event",i),Xt.emit(`track:${n}`,i),i}async function U(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?no.join(bu,"events"):ro(t));let o;try{o=await yu.readFile(vw(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
72
|
+
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function Tw(t,e){let n=`track:${t}`;return Xt.on(n,e),()=>Xt.off(n,e)}var a_,i_,c_,Xt,bu,vw,Oi,ut=M(()=>{a_=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","handoff.artifact","code.pushed","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.circuit.tagged","blueprint.item.bound","changelog.published","stall.detected","error"]),i_=new Set(["user","agent","orchestrator","system","peer"]),c_=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Xt=new o_;Xt.setMaxListeners(0);bu=process.env.BOSTRAT_STATE_DIR||no.join(process.env.HOME||r_.tmpdir(),".bostrat");vw=(t,e)=>no.join(t,`${e}.ndjson`),Oi=new Map});function d_(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===`
|
|
73
|
+
`){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function Rs(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 $s(t){let e=t;for(;e.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(e[0]);)e=e.slice(1);for(;e.length&&(e[0]==="sudo"||e[0]==="time"||e[0]==="env");)e=e.slice(1);return e}function gt(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(_w(o)){e.has(o)&&r++;continue}n.push(o)}return n}function Iw(t){let e=t.indexOf("-n"),n=e!==-1?t[e+1]:null;if(!n)return null;let r=/^(\d+),(\d+)p$/.exec(n)||/^(\d+)p$/.exec(n);return r?r[2]?{from:r[1],to:r[2]}:{from:r[1],to:r[1]}:null}function m_(t){let e=Iw(t),n=jt(gt(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${vt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${vt(n)}`:t.includes("-i")&&n?`Edit ${vt(n)}`:null}function ku(t){if(t.includes("--files")){let s=jt(gt(t,Aw)[0]);return s&&s!=="."?`List files in ${oo(s)}`:"List files"}let e=gt(t,Aw),n=e[0];if(!n)return null;let r=jt(e.slice(1).find(s=>s!==".")),o=so(n,40);return r?`Search for \u201C${o}\u201D in ${oo(r)}`:`Search for \u201C${o}\u201D`}function xu(t){let e=gt(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 Su(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=gt(t)[0];return n?`Run ${vt(n)}`:null}function h_(t){let e=gt(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 Ew(t){return t.find(e=>/^https?:\/\//.test(e))||null}function y_(t){let e=$s(Rs(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(vt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return vu(e[2]);let n=vt(e[0]);if(g_.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=w_[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function vu(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
|
|
74
|
+
`,n);i!==-1&&(e=e.slice(0,i))}let r=d_(e);for(let i=0;i<r.length-1;i++){let c=$s(Rs(r[i])),l=$s(Rs(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(vt(c[0]))&&vt(l[0])==="sed"){let u=Iw(l),f=gt(c)[0];if(u&&f)return so(u.from===u.to?`Read line ${u.from} of ${vt(f)}`:`Read lines ${u.from}\u2013${u.to} of ${vt(f)}`,88)}}let o=[];for(let i of r){let c=y_(i);c&&o.push({d:c,head:vt($s(Rs(i))[0]||""),sub:gt($s(Rs(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":so(s?s.d:o[0].d,88)}var so,jt,vt,oo,_w,Aw,f_,p_,w_,g_,Rw=M(()=>{so=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,jt=t=>t&&!/[$`]/.test(t)?t:null,vt=t=>t.replace(/\/+$/,"").split("/").pop()||t,oo=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},_w=t=>t.startsWith("-"),Aw=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);f_={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 ${vt(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"},p_={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"};w_={git:t=>{let e=gt(t,new Set(["-C","-c"])),n=f_[e[0]];return n?n(e,t):null},gh:t=>{let e=gt(t),n=p_[e[0]];return n?n(e):null},sed:m_,rg:ku,grep:ku,egrep:ku,nl:t=>{let e=jt(gt(t)[0]);return e?`Read ${vt(e)}`:null},cat:t=>{let e=jt(gt(t)[0]);return e?`Read ${vt(e)}`:null},head:t=>{let e=jt(gt(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${vt(e)}`:null},tail:t=>{let e=jt(gt(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${vt(e)}`:null},wc:t=>{let e=jt(gt(t)[0]);return e?`Count lines in ${vt(e)}`:"Count input lines"},ls:t=>{let e=jt(gt(t)[0]);return e&&e!=="."?`List files in ${oo(e)}`:"List files"},find:t=>jt(t[1])&&!_w(t[1])&&t[1]!=="."?`Find files in ${oo(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=jt(gt(t)[0]);return e?`Create directory ${oo(e)}`:null},touch:t=>{let e=jt(gt(t)[0]);return e?`Create ${vt(e)}`:null},rm:t=>{let e=jt(gt(t)[0]);return e?`Delete ${oo(e)}`:null},mv:t=>{let e=jt(gt(t)[0]);return e?`Move ${vt(e)}`:null},cp:t=>{let e=jt(gt(t)[0]);return e?`Copy ${vt(e)}`:null},chmod:t=>{let e=jt(gt(t)[1]);return e?`Change permissions on ${vt(e)}`:null},diff:t=>{let e=gt(t);return e.length>=2?`Compare ${vt(e[0])} and ${vt(e[1])}`:null},curl:t=>{let e=Ew(t);return e?`Fetch ${so(e,56)}`:"Fetch a URL"},wget:t=>{let e=Ew(t);return e?`Fetch ${so(e,56)}`:"Fetch a URL"},npm:xu,pnpm:xu,yarn:xu,npx:t=>{let e=gt(t)[0];return e?`Run ${e}`:null},node:t=>Su(t,"Node"),python:t=>Su(t,"Python"),python3:t=>Su(t,"Python"),make:t=>{let e=gt(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:h_,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=gt(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=gt(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=gt(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},g_=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function ao(t){let e=t.split(`
|
|
75
|
+
`).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function _n(t,e=""){let n=e.trim();return n||vu(t)||ao(t)}function Tu(t,e){if(t){if(Array.isArray(t)){for(let n of t)Tu(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&Au.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))Tu(n,e)}}}async function $w(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Yt(t,e)}catch{return[]}let o=[];for(let s of r.split(`
|
|
76
|
+
`)){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(Tu(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:_n(f.command,f.description),ts:u})}return o.slice(-n)}function Ow(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,w)=>w>=s&&(m.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let p=d.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:p,ts:Math.max(...p.map(m=>m.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var Au,io=M(()=>{ge();Rw();Au=new Set(["Bash"])});import Cw from"node:fs";import _u from"node:fs/promises";import Eu from"node:path";import b_ from"node:os";async function Mw(t){try{return await _u.realpath(t)}catch{return t}}function x_(t=k_){let e=[],n=r=>{let o;try{o=Cw.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Eu.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:Cw.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function S_(t,e=1048576){let n;try{n=await _u.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(`
|
|
77
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function v_(t){let e=await S_(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 pr(t,e){if(!t&&!e)return null;let n=x_();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await Mw(t);for(let o of n){let s=await v_(o.file);if(s&&(s===t||await Mw(s)===r))return o}return null}async function Nw(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await pr(t,e);if(!o)return[];let s;try{s=await Yt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
|
|
78
|
+
`)){let p=d.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let x=m.payload?.type;x==="task_started"?(l=!0,u=!1,f=m.payload?.turn_id||null):x==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Ru,sourceId:m.payload?.turn_id||f||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let w=m.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,Iu.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let y=A_(w.content),k=c.length?c.slice():null;c=[],!(!y&&!k)&&a.push({role:"assistant",text:y,tools:k||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}function E_(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 __(t){let e=t[0];if(e!=='"'&&e!=="'"&&e!=="`")return null;let n="";for(let r=1;r<t.length;r++){let o=t[r];if(o==="\\"){let s=t[++r];if(s===void 0)return null;if(s==="n")n+=`
|
|
79
|
+
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function I_(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=__(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Pw(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await pr(t,e);if(!o)return[];let s;try{s=await Yt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
80
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let f=[];if(u.type==="function_call"&&Os.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=E_(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=I_(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:_n(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Bw(t,e){let n=await pr(t,e);if(!n)return null;try{return Math.round((await _u.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var k_,T_,A_,Os,Iu,Ru,co=M(()=>{ge();io();k_=process.env.BOSTRAT_CODEX_SESSIONS||Eu.join(process.env.CODEX_HOME||Eu.join(b_.homedir(),".codex"),"sessions");T_=new Set(["output_text","text"]),A_=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&T_.has(e.type)).map(e=>e.text).join(`
|
|
81
|
+
`).trim():"";Os=new Set(["exec_command","shell","exec"]),Iu=new Set(["wait","write_stdin"]),Ru="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 Cs(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
|
|
82
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Dw.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Dw.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(R_);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 It(t){return String(t||"").replace(/\s+/g," ").trim()}function jw(t,e,{slackMs:n=2e4}={}){let r=It(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&&It(o.text).includes(r))}function Lw(t,e,n,{busy:r=!1}={}){let o=Cs(t,n);return o&&$u(e,o)?"stuck":r||o?"submitted":"unknown"}function $u(t,e){let n=It(t),r=It(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var Dw,R_,Ou=M(()=>{Dw=/^\s*─{8,}\s*$/,R_=/^❯[ ]?(.*)$/});function P_(t){return $_.test(String(t||"").trimStart())}function Hw(t){let e=String(t||"");if(!P_(e.trimStart()))return null;let n=e.replace(O_,"").trim(),r=B_(n),o=[];for(let u of M_){let f=u.from.map(d=>lo(r.get(d))).find(d=>d&&!Fw(d));f&&o.push({h:u.h,text:Ms(f,C_)})}let s=lo(r.get("Current Work"))||lo(r.get("Primary Request and Intent"))||j_(n),a=Mi(s),i=Ms(Ci(Nu(a[0]||"")),N_)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Ms(Ci(Nu(l)),Mu));for(let u of["Pending Tasks","Optional Next Step"]){let f=Mi(lo(r.get(u)))[0]||"";if(!(!f||Fw(f))){c.push(Ms(`Left to do: ${Ci(Nu(f))}`,Mu));break}}if(!c.length){let u=Mi(lo(r.get("Problem Solving"))||"")[0];u&&c.push(Ms(Ci(u),Mu))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:e.length}}function B_(t){let e=new Map,n=[];Cu.lastIndex=0;for(let r=Cu.exec(t);r;r=Cu.exec(t))n.push({name:D_(r[1]),start:r.index,end:r.index+r[0].length});for(let r=0;r<n.length;r++){let o=n[r+1]?.start??t.length;e.has(n[r].name)||e.set(n[r].name,t.slice(n[r].end,o).trim())}return e}function D_(t){return Uw.find(n=>n.toLowerCase()===t.toLowerCase())||t}function lo(t){return t?String(t).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
83
|
+
`).map(e=>e.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(e=>e&&!/^#{1,6}[ \t]/.test(e)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function j_(t){let e=t.replace(/^[\s\S]*?\bSummary:\s*/i,"");return lo(e.split(/\n{2,}/).slice(0,2).join(" "))}function Mi(t){let e=String(t||"").trim();if(!e)return[];let r=e.replace(/\b([A-Za-z0-9_-]+)\.(js|ts|tsx|mjs|json|md|css|jsonl|sh|yml|yaml|py|html)\b/g,"$1<DOT>$2").replace(/\b(e\.g|i\.e|etc|vs|approx|no|fig|Dr|Mr|Ms|St)\./gi,"$1<DOT>").split(/(?<=[.!?])["')\]]?\s+(?=[A-Z"“'(\[]|\d)/).map(s=>s.replace(/<DOT>/g,".").trim()).filter(Boolean),o=[];for(let s of r){let a=o[o.length-1];a&&(a.match(/["“”]/g)?.length??0)%2===1?o[o.length-1]=`${a} ${s}`:o.push(s)}return o}function Nu(t){return String(t||"").replace(/^(?:Immediately before (?:this|the) summary(?: request)?[,:]?\s*|At the (?:time|point) of (?:this )?compaction[,:]?\s*|Currently[,:]?\s*|The (?:most recent|immediately preceding|current) work (?:was|is)\s*)/i,"").replace(/^(?:I(?:'m|'d| am| was| had| have)?\s+(?:currently\s+|just\s+)?(?:been\s+)?(?:working on|in the middle of|completing|finishing)\s+|The user(?:'s)?\s+(?:most recent\s+)?(?:request|ask) (?:was|is)\s+(?:to\s+)?)/i,"").replace(/^I(?:'m|'d| am| was| had| have)?\s+(?:just\s+|then\s+)?(?=(?:[a-z]+(?:ed|ing)|ran|wrote|built|made|began|found|left|sent|took|went|did|got|kept|held|brought|caught|chose|came|saw|said|gave|told|set|put|read)\b)/,"").replace(/^["“]|["”]$/g,"").replace(/^(\w)/,e=>e.toUpperCase()).trim()}function Ci(t){return String(t||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,e=>`\u2026/${e.split("/").slice(-2).join("/")}`)}function Fw(t){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(t||"").trim())}function Ms(t,e){let n=String(t||"").trim();if(n.length<=e)return n;let r=Mi(n),o="";for(let i of r){let c=o?`${o} ${i}`:i;if(c.length>e)break;o=c}if(o.length>=Math.min(60,e*.4))return o;let s=n.slice(0,e),a=s.lastIndexOf(" ");return`${(a>e*.6?s.slice(0,a):s).replace(/[,;:.\s]+$/,"")}\u2026`}var $_,Uw,Cu,O_,C_,M_,N_,Mu,Ww=M(()=>{$_=/^This session is being continued from a previous conversation that ran out of context\./,Uw=["Primary Request and Intent","Key Technical Concepts","Files and Code Sections","Errors and Fixes","Problem Solving","All user messages","Pending Tasks","Current Work","Optional Next Step"],Cu=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Uw.join("|")})[:*\t ]*$`,"gim"),O_=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,C_=420,M_=[{h:"What we were doing",from:["Primary Request and Intent"]},{h:"Key decisions",from:["Key Technical Concepts"]},{h:"Where it got to",from:["Current Work","Problem Solving"]},{h:"Left to do",from:["Pending Tasks","Optional Next Step"]}],N_=116,Mu=168});function Kw(t){return Pu.has(String(t||"").replace(/^\//,"").toLowerCase())}function Ni(t){let e=String(t||"").trim();if(!e)return null;let n=Hw(e);if(n)return n;let r=e.match(L_);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=e.match(F_);if(o)return{kind:"skill",name:o[1]};let s=e.match(U_);if(s){let i=s[1].toLowerCase();return Pu.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=e.match(X_);if(a&&Pu.has(a[1].toLowerCase()))return{kind:"suppress"};if(H_.test(e))return{kind:"suppress"};if(q_.test(e))return{kind:"suppress"};if(W_.test(e))return{kind:"suppress"};if(K_.test(e)){let i=l=>Z_(e.match(l)?.[1].trim()||"")||null,c=i(Y_);return{kind:"task",summary:i(G_)||"",taskId:i(V_),toolUseId:i(z_),status:i(J_)?.toLowerCase()||null,resultHead:c?c.length>qw?`${c.slice(0,qw).trimEnd()}\u2026`:c:null}}return null}function Z_(t){return t.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Gw(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":t?.kind==="resume-summary"?t.title?`Context resumed from summary: ${t.title}`:"Context resumed from summary":""}function Vw(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":t?.kind==="resume-summary"?"context-resumed":"command-run"}var L_,F_,U_,H_,W_,q_,K_,G_,V_,z_,J_,Y_,qw,Pu,X_,Bu=M(()=>{Ww();L_=/^Base directory for this skill:\s*(\S+)/,F_=/^Run the "([^"]+)" workflow\./,U_=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,H_=/^<local-command-(?:stdout|caveat)>/i,W_=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,q_=/^Continue from where you left off\.$/,K_=/^<task-notification>/i,G_=/<summary>([\s\S]*?)<\/summary>/i,V_=/<task-id>([\s\S]*?)<\/task-id>/i,z_=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,J_=/<status>([\s\S]*?)<\/status>/i,Y_=/<result>([\s\S]*?)(?:<\/result>|$)/i,qw=600,Pu=new Set(["model","clear","compact","login","exit","quit"]);X_=/^\/([a-z0-9:_-]+)$/i});import Q_ from"node:crypto";function rI(t){let e=Array.isArray(t)?t:Array.isArray(t?.questions)?t.questions:null;if(!e||!e.length)return null;let n=[];for(let r of e.slice(0,Jw)){let o=Ns(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,tI)){let c=Ns(i?.label);if(!c)return null;let l=Ns(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<eI)return null;n.push({question:o,header:Ns(r?.header).slice(0,nI),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function ju(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;zw.lastIndex=0;let e=null;for(let o;o=zw.exec(t);)e=o;if(!e||e.index+e[0].length<t.trimEnd().length)return null;let n;try{n=JSON.parse(e[2])}catch{return null}let r=rI(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function Lu(t,e,n){let r=Q_.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function Fu(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=Jw}={}){if(!Array.isArray(t)||!e||!Du.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||ju(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Lu(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 Yw(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=Ns(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
84
84
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
85
85
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
86
|
-
${o}`),s||""}var
|
|
86
|
+
${o}`),s||""}var Du,Jw,tI,eI,nI,zw,Ns,Xw,Uu=M(()=>{Du=new Set(["cursor","codex"]),Jw=4,tI=4,eI=2,nI=24,zw=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Ns=t=>typeof t=="string"?t.trim():"";Xw=`
|
|
87
87
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
88
88
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
89
89
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -103,62 +103,66 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
103
103
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
104
104
|
anything you can decide yourself; keep asking in prose for questions without
|
|
105
105
|
enumerable options.
|
|
106
|
-
`});function B_(t){return{id:t.id,kind:"prompt",groupId:t.groupId,provider:t.provider,box:t.box,agent:t.provider,questionTotal:t.questions.length,answeredVia:"chat",questions:t.questions.map((e,n)=>({index:n,header:e.header||"",question:e.question,multiSelect:!!e.multiSelect,chosen:[]})),ts:t.ts}}function j_(t,e,n){let r=Rw(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 lr(t){let e=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,d=new Map;for(let g of t||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let h=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let T of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let _=T[0].split("/").pop().toLowerCase();d.has(_)||d.set(_,[]),d.get(_).push(g.payload?.ts??g.ts)}}let f=3*6e4,p=20*6e4,m=(g,h)=>!!(g?.name&&d.get(String(g.name).toLowerCase()))?.some(_=>_>=h-f&&_<=h+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let h=It(g.payload?.text);h&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(h))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let h=It(g.payload?.text);h&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(h))}else if(g.type==="job.started"&&g.payload?.id)a.add(g.payload.id),(g.payload.kind||"agent")==="agent"&&g.payload.name&&c.add(g.payload.name);else if(g.type==="turn.user"&&g.payload?.note?.kind==="task"){let h=g.payload.note,T={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:ot(g)};h.toolUseId&&i.set(h.toolUseId,T);let _=String(h.summary||"").match(u);_&&l.set(_[1],T)}let w=new Map,y=new Map,k=null,S=new Map,$=new Map,R=new Map,J=new Map,C=new Map,D=new Map,A=new Map,N=new Map,W=new Map,at=new Map,it=new Map,E=()=>{for(let g of it.values())g.questions.length&&e.push(g);it.clear()},O=null,At=6e4,Gt=new Map,gt=(g,h)=>{let T=o.get(g);if(!T?.length)return!1;let _=It(h),St=s.get(g);return T.some(nt=>{if(!_.startsWith(nt))return!1;let dt=It(_.slice(nt.length));return!!dt&&!!St?.has(dt)})};for(let g of t||[]){let h=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(h.text||"").trim()&&!h.images?.length)break;{let T=h.note||Si(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"&&Sw(T.command))break;if(T.kind==="resume-summary"){e.push({id:g.id,kind:"resume-summary",title:T.title||"Context resumed from summary",bullets:T.bullets||[],sections:T.sections||[],rawChars:T.rawChars??null,ts:ot(g),box:g.boxId,agent:g.provider});break}T.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:Tw(T),name:T.name??null,command:T.command??null,summary:T.summary??null,status:T.status??null,ts:ot(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${ot(g)}`)||g.source==="transcript"&>(g.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:ot(g),box:g.boxId,agent:g.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(O=null);break}for(let T of S.values())T.status="stale";S.clear();for(let T of R.values())T.status="stale";R.clear(),O&&O.box===g.boxId&&(e.push(B_(O)),O=null),e.push({id:g.id,kind:"message",role:"user",text:h.text,ts:ot(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...g.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let T=h.from||{};e.push({id:g.id,kind:"event",type:"peer-message",text:h.text,from:T.trackName||T.boxId||T.name||null,fromTrackId:T.trackId||null,ts:ot(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;E();let T=(h.images||[]).filter(dt=>!m(dt,ot(g)));if(!String(h.text||"").trim()&&!T.length)break;if(g.provider==="cursor"){let dt=`${g.boxId}|${h.text}`;if(n.has(dt))break;n.add(dt)}(!O||O.box===g.boxId)&&(O=null);let _=g.provider==="codex"?(h.tools||[]).filter(dt=>!bu.has(dt)&&!Ss.has(dt)):h.tools||[],St=h.text,nt=null;if(Iu.has(g.provider)){let dt=Ru(String(h.text||""));dt&&(St=dt.text,nt={questions:dt.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:St,ts:ot(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...nt?{ask:nt}:{},..._.length?{tools:_}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...T.length?{images:T}:{}}),nt&&(O={id:g.id,groupId:$u(g.provider,h.sourceId||g.id,nt.questions),provider:g.provider,box:g.boxId,questions:nt.questions,ts:ot(g)});break}case"job.started":{if(!h.id)break;let T=ot(g),_=i.get(h.id);if(!_&&(h.kind||"agent")==="agent"&&h.name){let Bt=l.get(h.name);Bt&&Bt.ts>=T&&(_=Bt)}let St={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:_?_.status==="killed"?"stopped":_.status&&_.status!=="completed"?"failed":"done":"running",startedTs:T,finishedTs:_?.ts??null,summary:_?.summary??null,resultHead:_?.resultHead??null},nt=Gt.get(g.boxId),dt=nt?.jobs[nt.jobs.length-1]?.startedTs;if(nt&&dt!=null&&T-dt<=At)nt.jobs.push(St);else{let Bt={id:g.id,kind:"job-dock",dockId:h.id,ts:T,box:g.boxId,agent:g.provider,jobs:[St]};Gt.set(g.boxId,Bt),e.push(Bt)}break}case"handoff.done":e.push(Re(g,"lead-handoff"));break;case"subtask.delegated":e.push({...Re(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(Re(g,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let _ of S.values())_.status="stale";S.clear();for(let _ of R.values())_.status="stale";R.clear()}let T=Re(g,`pr-${h.change}`);if(T.box=g.boxId??null,D_.has(h.change)&&h.number!=null){let _=j_(w,y,h);if(w.size>=2){for(let nt of y.values())for(let dt of nt)dt.grouped=!0;T.groupPrs=[...w.values()];let St=[T];for(let nt of w.keys())y.set(nt,St)}else{let St=y.get(_)??[];St.push(T),y.set(_,St)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let _=Rw(h),St=k&&!k.complete||w.size>=2;if(w.has(_))w.delete(_),y.delete(_);else if(h.number!=null)for(let nt of[...w.keys()])nt.endsWith(`#${h.number}`)&&(w.delete(nt),y.delete(nt));if(St&&h.number!=null){(!k||k.complete)&&(k={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:ot(g),ts:ot(g),box:g.boxId,agent:g.provider},e.push(k)),k.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:ot(g)}),k.remaining=[...w.values()],k.complete=w.size===0;break}}e.push(T);break}case"pr.offered":{if(!h.offerId)break;for(let _ of S.values())_.status="stale";S.clear();for(let _ of R.values())_.status="stale";R.clear();let T={id:g.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:ot(g),box:g.boxId,agent:g.provider};S.set(h.offerId,T),$.set(h.offerId,T),e.push(T);break}case"pr.offer.answered":{let T=$.get(h.offerId);T&&(T.status=h.action||"dismiss",T.actions=Array.isArray(h.actions)?h.actions:null,T.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(T.existingPr={number:h.existingPr.number??null}),S.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let _ of R.values())_.status="stale";R.clear();let T={id:g.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:ot(g),box:g.boxId,agent:g.provider};R.set(h.offerId,T),J.set(h.offerId,T),e.push(T);break}case"pr.exists.answered":{let T=J.get(h.offerId);T&&(T.status=h.choice||"dismiss",R.delete(h.offerId));break}case"box.state.changed":{let T=h.change==="model"?"model-change":h.change;if(!T)break;let _=Re(g,T);T==="model-change"&&(_.to=h.model),e.push(_);break}case"view.state.changed":{if(h.change!=="created"||h.restarted)break;e.push({id:g.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:ot(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:ot(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(h.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:ot(g),box:g.boxId,agent:g.provider});break}if(h.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:ot(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:ot(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:ot(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:ot(g),box:g.boxId,agent:g.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let _ of A.values())_.box===(h.box||g.boxId)&&(_.status="stale",A.delete(_.offerId));let T={id:g.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:ot(g),box:h.box||g.boxId,agent:h.agent||g.provider};A.set(h.offerId,T),N.set(h.offerId,T),e.push(T);break}case"box.fullscreen.answered":{let T=N.get(h.offerId);T&&(T.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",A.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let _ of C.values())_.key===h.key&&(_.status="stale",C.delete(_.offerId));let T={id:g.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:ot(g),box:g.boxId,agent:g.provider};C.set(h.offerId,T),D.set(h.offerId,T),e.push(T);break}case"ticket.move.answered":{let T=D.get(h.offerId);T&&(T.status=h.action==="moved"?"moved":"dismiss",C.delete(h.offerId));break}case"ticket.state.changed":{for(let T of C.values())T.key===h.key&&T.to===h.to&&(T.status="stale",C.delete(T.offerId));if(h.mode==="offer")break;e.push(Re(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:ot(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(Re(g,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let _ of W.values())_.status="stale";W.clear();let T={id:g.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:ot(g),box:g.boxId,agent:g.provider};W.set(h.proposalId,T),at.set(h.proposalId,T),e.push(T);break}case"ticket.create.answered":{let T=at.get(h.proposalId);T&&(T.status=h.action==="created"?"created":"dismiss",T.createdCount=h.createdCount??null,T.failedCount=h.failedCount??null,W.delete(h.proposalId));break}case"ticket.created":{let T={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},_=h.proposalId?at.get(h.proposalId):null;if(_){_.created=T;break}e.push({id:g.id,kind:"ticket-created",...T,ts:ot(g),box:g.boxId,agent:g.provider});break}case"error":{if(!h.kind)break;e.push(Re(g,h.kind));break}case"usage.cap.hit":e.push(Re(g,"usage-cap-hit"));break;case"usage.cap.warning":e.push(Re(g,"usage-cap-warning"));break;case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:h.provider||g.provider,ts:ot(g),at:ot(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(O=null,h.control||!h.question)break;let T=h.groupId||h.promptId;if(!T)break;let _=it.get(T);_||(_={id:g.id,kind:"prompt",groupId:T,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:h.questionTotal??1,questions:[],ts:ot(g)},it.set(T,_));let St=h.questionIndex??0,nt={index:St,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},dt=_.questions.findIndex(Bt=>Bt.index===St&&It(Bt.question)===It(nt.question));dt>=0?_.questions[dt]=nt:_.questions.push(nt),_.questions.sort((Bt,K)=>Bt.index-K.index),_.questionTotal=Math.max(_.questionTotal,h.questionTotal??1),_.questions.length>=_.questionTotal&&(e.push(_),it.delete(T));break}default:break}}return F_(e.sort((g,h)=>(g.ts??0)-(h.ts??0)))}function F_(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<=L_){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 Mu(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:U_(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??lr(t)).filter(o=>o.kind==="message");return cw(n,r)}function U_(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Xr(t.command)?e:An(t.command,t.description||"")}function Re(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:ot(t),ts:ot(t)}}function $w(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+H_,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&W_.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}function Ow(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||!K_.has(r.type)||!r.box)continue;let o=(r.ts??0)+q_,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var ot,D_,Rw,L_,H_,W_,q_,K_,Pu=M(()=>{Zr();Qr();Su();_u();Cu();ot=t=>t.payload?.ts??t.ts;D_=new Set(["opened","ci-fixed","reconciled","pushed"]),Rw=t=>`${t.repo??""}#${t.number}`;L_=2*6e4;H_=12e4,W_=new Set(["archived","unarchived","opened"]);q_=2500,K_=new Set(["pr-opened","pr-merged","pr-auto-armed"])});function Y_(t){let e=null,n=null,r=null;for(let o of t??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(e={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:e,lastPush:n,lastEventAt:r}}function Z_(t){for(let e=(t?.length??0)-1;e>=0;e--){let n=t[e];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function Q_(t,e){let n=[],r=[],o=new Set,s=new Set;for(let a=(t?.length??0)-1;a>=0;a--){let i=t[a];if(i?.type!=="tool.called")continue;if(e!=null&&(i.ts??0)<=e)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<z_&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<J_&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Nw(t,{trackName:e="this Track",now:n=null,mode:r="failover"}={}){let o=Y_(t),s=Z_(t),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=lr(t??[]).filter(f=>f?.kind==="message"&&typeof f.text=="string"&&f.text.trim()).slice(-G_);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${e}`),l.push(""),l.push(Pw[r]??Pw.failover),l.push(""),o.lastPush?.sha){let f=Mw(n,o.lastPush.ts),p=f?` (~${f} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let f=Mw(n,i);l.push(`## Last real checkpoint (written by the previous agent${f?`, ~${f} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=Q_(t??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(f=>`\`${f}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(f=>`\`${f}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let f of c)l.push(`**${f.role==="user"?"operator":"agent"}:** ${X_(f.text,V_)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let d=l.join(`
|
|
107
|
-
`);return
|
|
108
|
-
|
|
109
|
-
[\u2026 brief truncated]`}var
|
|
110
|
-
|
|
111
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function lI(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Lw.test(s)){if(r.length>=Jw)continue;let a=cI(s.replace(Lw,"").replace(/[.;,:]+$/,""),Yw,aI);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>jw&&(o=`${o.slice(0,jw-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Xw(t,e,{timeoutMs:n=9e4}={}){let r=_n.join(e,oI),o=_n.join(r,sI);await En.mkdir(r,{recursive:!0});let s=await vi(o),a=await tt(t);if(!a)throw new Error(`session "${t}" not found`);return await Ti(t,a,iI(o)),await ag(o,s,n),lI(await En.readFile(o,"utf8"))}function Zw(t,e){let n=(e||"").trim();if(!n)return!1;if(t==="assistant")return n===Kw||n===zw;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(qw)||n===eI||r.startsWith(Gw)||r.startsWith(Vw)}function Qw(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-uI;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 tg(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 eg(t,{windows:e=[]}={}){if(Zw(t.role,t.text))return!0;if(t.role!=="assistant"||!t.ts)return!1;for(let n of e)if(t.box===n.box&&t.ts>=n.from&&t.ts<=n.to)return!0;return!1}function Ai(t){return Nu.get(t)||null}async function ng(t){let e=Nu.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await tt(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);let r={name:t,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:_n.join(n.cwd,Uw,Hw)};return Nu.set(t,r),pI(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function pI(t,e){t.step="summarize",await rg(t.name,e),t.step="clear";let n=await tt(t.name);await Ti(t.name,n,"/clear"),await Bu(dI),t.step="resume",await Ti(t.name,n,rI),t.step="done",t.finishedAt=Date.now()}async function rg(t,e,{timeoutMs:n=fI}={}){let r=_n.join(e,Uw),o=_n.join(r,Hw);await En.mkdir(r,{recursive:!0});let s=await vi(o),a=await tt(t);await G({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:o,beforeMtime:s}),await Ti(t,a,nI(o)),await ag(o,s,n),await G({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:o,afterMtime:await vi(o)});let i=await Zh(e,o,{exclude:u=>Zw(u.role,u.text)});await G({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(c){await I({id:c,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await En.readFile(o,"utf8");await I({id:c,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Ww(u),ts:Date.now()}})}catch{}}await Ym(e);let l=await mI(r,o);return c&&await ih(c,l||o,{box:t,agent:a?.agent??null}),o}async function og(t,{timeoutMs:e}={}){let n=await tt(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);return rg(t,n.cwd,e?{timeoutMs:e}:void 0)}async function sg(t,e,{trackName:n="this Track"}={}){let r=await L({id:t,cwd:e}),o=Nw(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=_n.join(e,Fw);return await En.mkdir(_n.dirname(s),{recursive:!0}),await En.writeFile(s,o,"utf8"),await I({id:t,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Ww(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Fw}async function vi(t){try{return(await En.stat(t)).mtimeMs}catch{return 0}}async function ag(t,e,n){let r=Date.now()+n;for(;;){if(await vi(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Bu(2e3)}}async function mI(t,e){try{if(!tI.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=_n.join(t,`${n}.md`);return await En.copyFile(e,r),r}catch{return null}}function Bu(t){return new Promise(e=>setTimeout(e,t))}async function Ti(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 tt(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await Tt(t,["Escape"],{kind:"drive-wipe"}),await Bu(150),await Gr(t,ai,{kind:"drive-wipe"}),await Mt(t,n,{kind:"orchestration-drive",...Ie(r.agent)})}var Uw,Hw,Dw,qw,Kw,Gw,eI,nI,rI,Vw,zw,oI,sI,jw,Jw,Yw,aI,iI,Lw,cI,uI,dI,fI,Nu,Fw,Du=M(()=>{Ht();hi();We();gs();Qh();Ya();ct();jl();Bw();Uw=".bostrat/handoff",Hw="latest.md",Dw=32*1024;qw="Checkpoint: write a work-in-progress handoff",Kw="HANDOFF WRITTEN",Gw="Resume from .bostrat/handoff/latest.md",eI="/clear",nI=t=>`${qw} 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 ${Kw} and take no further action.`,rI=`${Gw}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Vw="Beat: write a",zw="BEAT WRITTEN",oI=".bostrat/arc",sI="beat.txt",jw=200,Jw=4,Yw=5,aI=36,iI=t=>`${Vw} 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 ${Jw} further lines, each starting with "- " and naming ONE piece in at most ${Yw} 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 ${zw} and take no further action.`,Lw=/^(?:[-–—*•·]|\d+[.)])\s+/,cI=(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};uI=6e5;dI=3500,fI=3e5,Nu=new Map;Fw=".bostrat/handoff/status-latest.md"});function cg(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!hI.test(String(t)))throw new Error(`pricing.js: ${n} window ${e} must be YYYY-MM-DD, got ${JSON.stringify(t)}`);let r=Date.parse(`${t}T${e==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${e} is not a real date: ${t}`);return r}function wI(t){let e={};for(let[n,r]of Object.entries(t)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);e[n]=r.map(o=>({...o,fromMs:cg(o.from,"from",n),toMs:cg(o.to,"to",n)}))}return e}function yI(t){if(t==null)return Date.now();if(typeof t=="number")return Number.isFinite(t)?t:null;if(t instanceof Date){let n=t.getTime();return Number.isFinite(n)?n:null}let e=Date.parse(String(t));return Number.isFinite(e)?e:null}function bI(t){if(!t)return null;let e=String(t).trim();return ju[e]?e:ig[e]?ig[e]:Object.keys(ju).find(r=>e.startsWith(r))||null}function lg(t,e){let n=bI(t);if(!n)return null;let r=yI(e);return r===null?null:gI[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function In(t,e={},n){let r=lg(t,n);return r?((e.input||0)*r.input+(e.output||0)*r.output+(e.cacheWrite||0)*r.cacheWrite+(e.cacheRead||0)*r.cacheRead)/1e6:0}var ju,ig,hI,gI,ug,Es=M(()=>{ju={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},ig={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},hI=/^\d{4}-\d{2}-\d{2}$/;gI=wI(ju);ug=(t,e)=>lg(t,e)!==null});function kI(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function fg(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function Ke(t){let e=t.payload?.model||"unknown",n=kI(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=t.source==="vendor",s=r,a=!1;if(n&&!o){let l=In(e,n,t.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!ug(e,t.ts);return{usd:s,model:e,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function Rn(t,{boxLabels:e={}}={}){let n=(t||[]).filter(f=>f.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=dg();for(let f of n){let{usd:p,model:m,tokens:w,repriced:y,unpriced:k,asRecorded:S}=Ke(f);i+=S;let $=f.provider||"unknown",R=f.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(m)),a+=p,fg(d,w),r[m]||={usd:0,tokens:dg(),events:0},r[m].usd+=p,r[m].events+=1,fg(r[m].tokens,w),o[$]||={usd:0,events:0},o[$].usd+=p,o[$].events+=1,s[R]||={usd:0,events:0,provider:$,models:new Set,label:e[R]||R},s[R].usd+=p,s[R].events+=1,m!=="unknown"&&s[R].models.add(m),f.epoch!=null){let J=(s[R].epochs||={})[f.epoch]||={usd:0,events:0,provider:$,models:new Set};J.usd+=p,J.events+=1,J.provider=$,m!=="unknown"&&J.models.add(m)}}return{totalUsd:eo(a),asRecordedUsd:eo(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Ei(r,f=>({...f,usd:eo(f.usd)})),byProvider:Ei(o,f=>({...f,usd:eo(f.usd)})),byBox:Ei(s,f=>({usd:eo(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:Ei(f.epochs,p=>({usd:eo(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var dg,eo,Ei,ur=M(()=>{Es();dg=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});eo=t=>Math.round(t*1e4)/1e4,Ei=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]))});function xI(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 pg(t,e=[]){let n=[],r=t?.events||[],o=new Map,s=0;for(let u of e){if(u.type!=="pr.state.changed")continue;let d=u.payload||{};!d.repo||d.number==null||(d.change==="merged"?o.set(`${d.repo}#${d.number}`,d):d.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(d=>{let[f,p]=d.split("#");return`${f.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Lu(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Lu(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(d=>`${d.fromAgent}\u2192${d.toAgent}`).join(", ");n.push(`${Lu(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Rn(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?xI(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Lu,mg=M(()=>{ur();Lu=(t,e)=>`${t} ${e}${t===1?"":"s"}`});import{execFile as SI}from"node:child_process";import{promisify as vI}from"node:util";import{randomUUID as TI}from"node:crypto";async function Fu(t,e){let n=Date.now()+e;for(;;){if(await Kr(t))return!0;if(Date.now()>n)return!1;await _i(EI)}}async function II(t,e){let n=wg[e]||wg.default;n.escape&&(await Tt(t,["Escape"],{kind:"swap-exit"}),await _i(150)),n.wipe&&await Gr(t,ai,{kind:"swap-exit"}),await ii(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Ie(e).submitDelayMs||0;await _i(Math.max(r,250)),await Tt(t,["Enter"],{kind:"swap-exit"})}async function RI(t,e){try{let{stdout:n}=await no(B,j("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function gg(t){await no(B,j("respawn-pane","-k","-t",t))}async function Uu(t,e={}){if(!bt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await tt(t);if(!r)throw new Error(`session "${t}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${t}"`);let o=r.agent||"agent",s=e.agent||kn(n),a=Number(e.exitTimeoutMs)||AI;await G({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Kr(t)&&(await II(t,o),!await Fu(t,a)&&(i=!0,await G({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await gg(t),!await Fu(t,hg))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&rr(n),l=c?Ua():null,u=await RI(t,"ANTHROPIC_API_KEY"),d=!1;try{l?(await no(B,j("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await no(B,j("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),d=!0)}catch(y){throw l?ei(y,l):y}if(d&&!i&&(await gg(t),!await Fu(t,hg)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,p=e.resumeSessionId||TI(),m=f?n:ql(n,p,t),w=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await mn(r.cwd),s==="codex"&&await Br(r.cwd),s==="cursor"&&await Dr(r.cwd),await sr(t),await no(B,j("set-option","-t",t,"@bostrat-sid",p)),await no(B,j("set-option","-t",t,"@bostrat-agent",s)),await _i(_I),await Mt(t,m,{kind:"swap-launch"}),await G({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:d}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:d}}var no,_i,AI,hg,EI,_I,wg,yg=M(()=>{Ht();Qo();Wa();qa();gs();We();no=vI(SI),_i=t=>new Promise(e=>setTimeout(e,t)),AI=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,hg=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,EI=250,_I=400,wg={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 bg(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Jt(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
112
|
-
`)){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
|
|
113
|
-
`).trim():"",
|
|
114
|
-
`),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
|
|
115
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
116
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
117
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
118
|
-
`),o=
|
|
119
|
-
`),o=
|
|
120
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
121
|
-
`)].reverse().find(a=>
|
|
122
|
-
`),o=
|
|
123
|
-
`),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(
|
|
124
|
-
`),o=
|
|
125
|
-
`).pop()||"")?!0:
|
|
126
|
-
`).pop()||""):t==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./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
|
|
127
|
-
${String(e||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
128
|
-
`).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
|
|
129
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
130
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
131
|
-
`),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
|
|
132
|
-
`),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
|
|
133
|
-
`);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
|
|
134
|
-
`),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 Ay(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&&!Sy.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=vy.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!IR.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Os(t,e,{timeoutMs:n=ER}={}){let r=Date.now()+n;for(;;){let o=await yt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await Di(_R)}}async function OR(t,e){return await Mt(t,`/model ${e.model}`,Ie("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function CR(t,e){await si(t),await Mt(t,"/model",{enter:!1}),await Di(250),await Tt(t,["Enter"],{kind:"model-switch"});let n=await Os(t,yy);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await Tt(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=$R(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await Mt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await Os(t,i=>by(i)||!yy(i)&&o(i)&&{done:o(i)});if(!s)throw await Tt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?Sy.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 Tt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await Mt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Os(t,i=>!by(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 MR(){let t=gy.join(process.env.CURSOR_CONFIG_DIR||gy.join(AR.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await TR.readFile(t,"utf8"))?.model||null}catch{return null}}async function PR(t,e){await si(t),await ii(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await Os(t,ky),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 Os(t,ky,{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 Tt(t,["Down"],{kind:"model-switch"}),await Di(250)}if(!n||!s||!o(s)){await Tt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await Tt(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 Tt(t,["Enter"],{kind:"model-switch"}),await Di(800);let a=await MR();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 Ey(t,e,n){return t==="codex"?CR(e,n):t==="cursor"?PR(e,n):OR(e,n)}var Di,ER,_R,xy,IR,RR,Sy,vy,_y=M(()=>{Ht();gs();Di=t=>new Promise(e=>setTimeout(e,t)),ER=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,_R=300,xy=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 5"},{alias:"sonnet",label:"Sonnet 5"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],IR=new Set([...xy.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),RR=[{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.6-terra",label:"GPT-5.6 Terra"},{alias:"gpt-5.6-luna",label:"GPT-5.6 Luna"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}],Sy=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),vy=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}]});function Li(t){let e=t||{},n=NR(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function NR(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:ee(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:ee(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:ee(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:ee(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered,checkpoint:t.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:ee(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:ee(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:ee(t.agent),payload:{...po(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:ee(t.agent||t.toAgent),payload:{kind:"subtask-failed",...po(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:ee(t.agent),payload:{change:"subtask-resumed",...po(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...po(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...po(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:ee(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...po(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:ee(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}function $y(t,e){let n=Li(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;xd(()=>I(r,n))}async function Oy(t){if(!(!t?.id||Iy.has(t.id))){Iy.add(t.id);try{let e=await L(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=Li(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(t,o)}}catch{}}}function po(t){let{type:e,at:n,...r}=t;return r}function Cy(t,e,n){!n?.promptId||kd.get(t.id)===n.promptId||(kd.set(t.id,n.promptId),xd(()=>I(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:ee(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 Sd(t,e,n,r={}){kd.delete(t.id),xd(()=>I(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:ee(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}var ee,ji,xd,Ry,Iy,kd,vd=M(()=>{ct();ee=t=>t||null;ji=Promise.resolve(),xd=t=>(ji=ji.then(t).catch(()=>{}),ji),Ry=()=>ji;Iy=new Set;kd=new Map});function FR(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function jy(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of FR(n).reverse()){if(!BR.test(r))continue;let o=r.endsWith("?")&&r.length<=LR&&jR.test(r);if(DR.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function Ly(t){let e=String(t||""),n=[];UR.test(e)&&n.push("commit"),HR.test(e)&&n.push("rebase"),WR.test(e)&&n.push("push");let r=KR.test(e);!r&&qR.test(e)&&n.push("pr");let o=null;if(r){let s=e.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||e.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function mo(t,{branch:e=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(t),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=e?` on this branch (${e})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${e?` (${e})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${e?` (${e})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let f=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(f+=` ${d.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(f+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),f}function Fy(t,{branch:e=null,base:n=null,prNumber:r=null,offeredRebase:o=!1}={}){let s=new Set(Array.isArray(t)?t:["commit","push","pr"]),a=e?` on this branch (${e})`:"",i=[];s.has("commit")&&i.push(`commit the work${a} with a clear commit message`),s.has("rebase")&&i.push(`rebase ${s.has("commit")?"the branch":`this branch${e?` (${e})`:""}`} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`),i.push("move the work to a fresh branch (branch off the current HEAD with a clear new name)"),i.push("push the new branch to the remote"),i.push(`open a pull request${n?` against ${n}`:""} from the new branch with a concise title and a description of what changed and why (use \`gh pr create\`)`);let c=[];s.has("commit")||c.push("don't create any new commits \u2014 ship what's already committed"),o&&!s.has("rebase")&&c.push("don't rebase \u2014 leave the branch's history as it is");let l=`Yes \u2014 go ahead, as a NEW pull request: ${i.join(", then ")}.`;return c.length&&(l+=` ${c.join(", and ").replace(/^./,u=>u.toUpperCase())}.`),l+=` Leave the existing open PR${r?` #${r}`:""}${e?` and its remote branch (${e})`:" and its remote branch"} exactly as they are \u2014 don't push ${e||"the old branch"} itself.`,l}function Uy(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}if(o.type==="pr.exists.answered"&&s.choice==="new"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"||o.type==="pr.exists.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&e-r.lastActivity<=GR&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Hy(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+VR||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Wy(t){let e=(Array.isArray(t)?t:[]).filter(r=>My[r]).map(r=>My[r]);return`The ship you were given the go-ahead for${e.length?` (${e.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}var Py,Ny,By,Dy,BR,DR,jR,LR,Fi,UR,HR,WR,qR,KR,GR,VR,My,ho=M(()=>{Py=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,Ny=/(?<!\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,By=/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,Dy=/(?<!\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,BR=new RegExp(`\\b(?:${Py}|${Ny}|${By}|${Dy})\\b`,"i"),DR=/\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,jR=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,LR=90;Fi=["commit","rebase","push","pr"],UR=new RegExp(`\\b(?:${Py})\\b`,"i"),HR=new RegExp(`\\b(?:${Dy})\\b`,"i"),WR=new RegExp(`\\b(?:${Ny})\\b`,"i"),qR=new RegExp(`\\b(?:${By})\\b`,"i"),KR=/\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;GR=3e4;VR=2500;My={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import zR from"node:fs";import Td from"node:fs/promises";import Vy from"node:path";import JR from"node:os";function Yy(t,e=!1){if(!e&&t-Ky<YR)return;Ky=t;let n=JSON.stringify({windowStart:wo,byCaller:Object.fromEntries(Ve),lastHour:On});Gy=Gy.then(()=>Td.mkdir(zy,{recursive:!0})).then(()=>Td.writeFile(`${Ui}.tmp`,n)).then(()=>Td.rename(`${Ui}.tmp`,Ui)).catch(()=>{})}function Xy(t){t-wo<Jy||(On={start:wo,total:Hi(Ve),byCaller:Object.fromEntries(Ve)},On.total&&console.log(`[github] ${On.total} calls last hour`,On.byCaller),wo=t,Ve=new Map,Yy(t,!0))}function ze(t="other",e=Date.now()){Xy(e),Ve.set(t,(Ve.get(t)||0)+1),Yy(e)}function Zy(t=Date.now()){return Xy(t),{windowStart:wo,total:Hi(Ve),byCaller:Object.fromEntries(Ve),lastHour:On,backoffUntil:yr>t?yr:null}}function Cn(t){return XR.test(String(t?.message||t||""))}function Mn(t,e=Date.now()){return yr<=e&&console.warn(`[github] rate limited \u2014 backing off ${qy/6e4} min:`,String(t?.message||t||"").slice(0,200)),yr=Math.max(yr,e+qy),yr}function Wi(t=Date.now()){return Math.max(0,yr-t)}var zy,Ui,Jy,qy,YR,wo,Ve,On,yr,Ky,Hi,Gy,XR,Cs=M(()=>{zy=process.env.BOSTRAT_STATE_DIR||Vy.join(process.env.HOME||JR.tmpdir(),".bostrat"),Ui=Vy.join(zy,"github-metrics.json"),Jy=60*6e4,qy=5*6e4,YR=6e4,wo=Date.now(),Ve=new Map,On=null,yr=0,Ky=0,Hi=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(zR.readFileSync(Ui,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Jy?(wo=t.windowStart,Ve=new Map(Object.entries(t.byCaller||{})),On=t.lastHour||null):Hi(new Map(Object.entries(t.byCaller||{})))&&(On={start:t.windowStart,total:Hi(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}Gy=Promise.resolve();XR=/rate limit|too quickly|abuse detection|HTTP 429/i});import ZR from"node:crypto";import qi from"node:fs/promises";import Qy from"node:path";import QR from"node:os";async function Et(){return Pn||(Ad??=qi.readFile(_d,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(Pn=t,!e&&wt()){let{records:n,source:r}=await $t("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;Pn[V(i.repo,i.number)]=i}Object.keys(Pn).length&&await Id().catch(()=>{})}}return Pn}).finally(()=>Ad=null),Ad)}async function Id(){let t=JSON.stringify({links:Pn},null,2),e=`${_d}.${process.pid}.tmp`;await qi.mkdir(tb,{recursive:!0}),await qi.writeFile(e,t),await qi.rename(e,_d)}async function Pt(t){Ed=Ed.then(Id,Id),await Ed.catch(()=>{});let e=Pn?.[t];e&&Ut("prlinks",{id:eb(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}async function ne({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 Et(),c=V(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:t$.has(a),firstSeen:Date.now()},await Pt(V(t,e)),{link:i[c],created:!0})}async function Ms(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||n.announced?null:(n.announced=!0,await Pt(V(t,e)),n)}async function Wt(t,e){return!t||e==null?null:(await Et())[V(t,e)]??null}async function Nn(t,e,n){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await Pt(V(t,e)),r):null}async function nb(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await Et())[V(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Pt(V(t,e)),o):null}async function rb(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await Et())[V(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Pt(V(t,e)),o):null}async function Ki(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Pt(V(t,e)),r)}async function Gi(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Pt(V(t,e)),n)}async function Vi(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Pt(V(t,e)),r)}async function zi(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Pt(V(t,e)),n)}async function ob(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(r.baseFoldAt=n,await Pt(V(t,e)),r):null}async function sb(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Pt(V(t,e)),n)}async function ab(t,e,n){if(!t||e==null||!n)return null;let r=(await Et())[V(t,e)];return r?(r.lastHeadSha=n,await Pt(V(t,e)),r):null}async function Ps(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Pt(V(t,e)),r):null}async function br(t,e,n=!0){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Pt(V(t,e)),r):null}async function Ns(t,e,n=!0){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Pt(V(t,e)),r):null}async function ib(t,e,n){if(!t||e==null)return null;let r=(await Et())[V(t,e)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Pt(V(t,e)),r}async function Bs(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Pt(V(t,e)),n)}async function cb(){return Object.values(await Et()).filter(t=>!e$.has(t.state))}async function kr(){return Object.values(await Et()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function lb(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Et(),r=0;for(let[o,s]of Object.entries(n))await Ut("prlinks",{id:eb(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var tb,_d,V,eb,Pn,Ad,Ed,t$,e$,Bn=M(()=>{bn();tb=process.env.BOSTRAT_STATE_DIR||Qy.join(process.env.HOME||QR.tmpdir(),".bostrat"),_d=Qy.join(tb,"pr-links.json"),V=(t,e)=>`${t}#${Number(e)}`,eb=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=ZR.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},Pn=null,Ad=null,Ed=Promise.resolve();t$=new Set(["api","observed","reconciled"]);e$=new Set(["merged","closed","lost"])});function Ji(t,e,n){let{state:r,reason:o}=ou(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of Ds)a-c.at>=ub()&&Ds.delete(i);Ds.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=Ds.get(t);return s?Date.now()-s.at>=ub()?(Ds.delete(t),!1):s.busy:!1}async function Rd(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await yt(t,{lines:Ct}).catch(()=>"");if(!Ji(t,e,r))return Wh(e,r)?{working:!0,prompted:!1}:n;if(ao(e,r)){let o=await Sn(t).catch(()=>r),s=wr({name:t,agent:e},o);if(s)return!$i({name:t,agent:e},s,o)||Xg(t)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function Yi(t,e){return(await Rd(t,e)).working}var ub,Ds,Xi=M(()=>{Ht();hi();od();ad();ub=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,Ds=new Map});import{execFile as n$}from"node:child_process";import{promisify as r$}from"node:util";async function $d(t,e,{midTurn:n=Yi,pollMs:r=o$,capMs:o=s$}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}function m$(t,e){let n=String(t||"").match(p$);return!!n&&n[1]!==e}function Od(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function mb(t){let e=String(t||""),n=e.match(l$);if(!n)return null;let r=Od(e,n.index);return d$.test(r)?null:{repo:r.match(pb)?.[1]??null,head:r.match(f$)?.[1]??null}}function hb(t){let e=String(t||""),n=e.match(u$);if(!n)return null;let r=Od(e,n.index),o=Od(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("-")){h$.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(pb)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function g$(t,e,{exclude:n=()=>!1}={}){let r=async i=>{ze("observer");let{stdout:c}=await db("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",w$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Cn(l)&&Mn(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=e-a$&&!n(c)),s=await Ae({slug:t}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function y$(t,e){if(e)return[e];let n=[];for(let r of wb(t)){let o=await st(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Ue().catch(()=>[])){let o=await st(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function wb(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 b$(t,e,n){let r=t.workspace?.branch,o=e?.head??null,s=c=>r&&c===r||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||m$(c.headRefName,t.id),i=[];for(let c of await y$(t,e?.repo??null)){let l;try{l=await g$(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await Wt(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===t.id&&d.announced)return{settled:!0};if(d&&d.trackId!==t.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==t.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function gb(t,e,{delays:n=fb,turnEnd:r}={}){let o=mb(e.command);if(!o)return null;let s=!!hb(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(Dn.has(a))return null;Dn.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await b$(t,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:f}=u,{link:p,created:m}=await ne({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await Ms(d,f.number))?null:(await $d(e.box,e.agent,r),await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{Dn.delete(a)}})();return c.catch(()=>Dn.delete(a)),c}function yb(t,e,{delays:n=fb,turnEnd:r,openedWait:o}={}){let s=hb(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(Dn.has(a))return null;Dn.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let d=[];if(c)d=[c];else{let m=wb(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await st(w).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=t.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(m,w)=>{let y=await Wt(c,m.number);return y&&y.trackId!==t.id?null:(y||await ne({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Ps(c,m.number),w&&((await L(t).catch(()=>[])).some($=>$.type==="pr.state.changed"&&($.payload?.change==="auto-armed"||$.payload?.change==="merged")&&$.payload?.number===m.number)||(await $d(e.box,e.agent,r),await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(C=>setTimeout(C,m));let w=[];for(let C of d)try{ze("observer");let{stdout:D}=await db("gh",["pr","view",String(l),"--repo",C,"--json",k$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Ae({slug:C})}}),A=JSON.parse(D||"null");A?.number&&w.push({slug:C,pr:A})}catch(D){Cn(D)&&Mn(D)}let y=w.find(C=>C.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let k=y.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return f(k,!0);continue}let S=await Wt(c,k.number);if(S&&S.trackId!==t.id)return null;S||await ne({repo:c,number:k.number,trackId:t.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await $d(e.box,e.agent,r);let $=await L(t).catch(()=>[]);if($.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="merged"&&C.payload?.number===k.number))return null;let R=C=>C.some(D=>D.type==="pr.state.changed"&&D.payload?.change==="opened"&&D.payload?.number===k.number),J=0;if(!R($)){if(mb(e.command)&&await Ms(c,k.number)){let C=Date.now();J=C+1,await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:C,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await Wt(c,k.number))?.announced){let C=o?.capMs??i$,D=o?.pollMs??c$,A=Date.now()+C;for(;Date.now()<A&&(await new Promise(N=>setTimeout(N,D)),!R(await L(t).catch(()=>[]))););}}return await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),J),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await Ye(c,k.number).catch(()=>{}),await Je(c,k.number).catch(()=>{}),await Nn(c,k.number,"merged"),k}return p?f(p,!1):null}finally{Dn.delete(a)}})();return i.catch(()=>Dn.delete(a)),i}var db,fb,o$,s$,a$,i$,c$,l$,u$,d$,pb,f$,p$,h$,w$,Dn,k$,bb=M(()=>{ct();Qn();Qt();Cs();Bn();xr();Xi();db=r$(n$),fb=[4e3,15e3,45e3],o$=2500,s$=5*6e4;a$=15e3,i$=9e4,c$=2e3,l$=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,u$=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,d$=/\s--web\b/,pb=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,f$=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,p$=/^track\/.+-([0-9a-f]{8})$/;h$=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);w$="number,url,title,headRefName,createdAt";Dn=new Set;k$="number,url,title,headRefName,state"});import x$ from"node:fs/promises";import Md from"node:path";import S$ from"node:os";function Cd(t,e){let n=new RegExp(`\\b${e}="([^"]*)"`).exec(t);return n?n[1]:null}function Zi(t){let e=v$.exec(String(t||""));if(!e)return null;let[,n,r]=e,o=Cd(n,"from"),s=o?o.replace(/^uds:/,""):null,a=kb(s);return{socket:s,pid:a,name:Cd(n,"from-name"),mode:Cd(n,"from-mode"),text:r.trim()}}function kb(t){let e=/(\d+)\.sock$/.exec(String(t||""));return e?Number(e[1]):null}function T$(){return process.env.CLAUDE_CONFIG_DIR?Md.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Md.join(S$.homedir(),".claude","sessions")}async function A$(t){let e=kb(t);if(!e)return null;try{let n=await x$.readFile(Md.join(T$(),`${e}.json`),"utf8"),r=JSON.parse(n);return{pid:e,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function xb(t,e){let n=await A$(t?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof e=="function")try{o=await e(n?.sessionId??null,r)??null}catch{o=null}return{socket:t?.socket??null,pid:n?.pid??t?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??t?.name??null,mode:t?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}var v$,Pd=M(()=>{v$=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});import{randomUUID as E$}from"node:crypto";function js(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Sb(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function vb(t){return`job|${t.box}|id:${t.id}`}async function Bd(t){return Nd.has(t.id)?Nd.get(t.id):(Qi.has(t.id)||Qi.set(t.id,L(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(js({role:o,sourceId:r.payload.sourceId})),n.keys.add(js({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(js({role:"user",sourceId:r.payload.sourceId})),n.keys.add(js({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(Sb({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(vb({id:r.payload.id,box:r.boxId}));return Nd.set(t.id,n),n}).finally(()=>Qi.delete(t.id))),Qi.get(t.id))}async function Tb(t,e){try{let n=(await Bd(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=js(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?Zi(r.text):null;if(s){await I(t,{type:"peer.message",actor:"peer",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:s.text,ts:r.ts,keyText:String(r.text||"").slice(0,40),from:await xb(s,Ls),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}if(await I(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch){let a=jy(r.text);if(a){let{offered:i,toPr:c}=Ly(a),l;if(t.workspace?.kind==="multi"){let u=await go(t).catch(()=>[]);l=u.length?u.map(d=>({name:d.name,slug:d.repo??null})):void 0}await I(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:E$(),ts:r.ts,excerpt:a,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:i||void 0,toPr:c||void 0,repos:l}})}}}}catch{}}async function Ab(t,e){try{let n=await Bd(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=Sb(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await I(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),gb(t,o),yb(t,o))}}catch{}}async function Eb(t,e){try{let n=await Bd(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=vb(o);n.keys.has(s)||(n.keys.add(s),await I(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var Nd,Qi,_b=M(()=>{ct();ho();bb();Pd();xr();Nd=new Map,Qi=new Map});function _$(t){let e=[],n=null;for(let r of String(t||"").split(`
|
|
135
|
-
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;e.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return e}async function I$(t,e){let n=`${t.id}:${e.name}`;if(!Dd.has(n)){let r=await L(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);Dd.set(n,o)}return Dd.get(n)}async function Ib(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await cr(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await I$(t,e),o=_$(await Jt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await I(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:In(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await I(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Dd,Rb=M(()=>{ct();Qr();we();Es();Dd=new Map});import tc from"node:fs";import Ld from"node:path";import R$ from"node:os";async function C$({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(O$,{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:$b})});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<$b)break}return r}function M$(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}function Cb(){try{return JSON.parse(tc.readFileSync(ec(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function N$(t){try{tc.mkdirSync(Ld.dirname(ec()),{recursive:!0});let e=ec()+".tmp";tc.writeFileSync(e,JSON.stringify(t)),tc.renameSync(e,ec())}catch{}}async function D$({fetchImpl:t,now:e=Date.now()}={}){if(jn.rows&&e-jn.at<P$)return jn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Cb(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await C$({sinceMs:o,cookie:n,fetchImpl:t});jn={at:e,rows:s.map(M$).filter(Boolean),error:null}}catch(s){jn={at:e,rows:null,error:String(s?.message||s)}}return jn.rows}async function j$(t,e){let n=`${t.id}:${e.name}`;if(!jd.has(n)){let r=await L(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);jd.set(n,o)}return jd.get(n)}async function Mb(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Ob())return;let r=await D$({fetchImpl:n});if(!r||!r.length)return;let o=await Rg(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await j$(t,e),a=Cb(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${t.id}:${e.name}:${l.id}`))continue;s.add(l.id),i.add(`${t.id}:${e.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=In(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await I(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}N$({sinceMs:c,seen:[...i].slice(-B$)})}catch{}}var $$,Ob,O$,$b,P$,jn,ec,B$,Fd,jd,Ud=M(()=>{ct();qu();Es();$$=process.env.BOSTRAT_STATE_DIR||Ld.join(process.env.HOME||R$.tmpdir(),".bostrat"),Ob=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,O$="https://cursor.com/api/dashboard/get-filtered-usage-events",$b=100;P$=5*6e4,jn={at:0,rows:null,error:null},ec=()=>Ld.join($$,"cursor-usage.json");B$=5e3;Fd=()=>({enabled:Ob(),lastPollAt:jn.at||null,error:jn.error}),jd=new Map});import Hs from"node:fs/promises";import Xt from"node:path";import L$ from"node:os";import F$ from"node:net";import{randomUUID as U$}from"node:crypto";import{execFile as H$}from"node:child_process";import{promisify as W$}from"node:util";function Y$(t){for(let e of zd(t))if(!Lb.test(e))return e;return null}function Fb(t){let e=zd(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function X$(t,e){let n=Fb(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Xt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Xt.resolve(t,n[r+1])}return t}function Q$(t){let e=!1,n=!1;for(let r of zd(t)){if(!e){if(Lb.test(r))continue;e=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Hd(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Hd(r))return`serves the filesystem root ("${r}")`;continue}if(Hd(r))return`serves the filesystem root ("${r}")`;if(Z$(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function eO(t,e){if(Xt.resolve(t)===Xt.resolve(e))return!0;let[n,r]=await Promise.all([nc(t),nc(e)]);return n===r}async function nO(t){let n=(await Hs.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 Ub(Xt.join(t,o.name,Kd))?`./${o.name}`:null))).filter(Boolean)}async function rO(t,e){let[n,r]=await Promise.all([nc(t),nc(e)]),o=Xt.relative(n,r);if(o&&(o.startsWith("..")||Xt.isAbsolute(o)))throw Fs(`serve directory escapes the Track workspace: ${e}`)}async function oO(t,e){let n=Q$(e);if(n)throw Fs(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await tO(t))throw Fs(`serve directory does not exist: ${t}`);let r=Y$(e);if(!r)return;if(!r.includes("/")&&!await hh(r,{loginShell:!0,failOpen:!0}))throw Fs(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(V$.test(e)||z$.has(r)||!G$.has(r)||!Fb(e).includes("run")||J$.test(e))return;let o=X$(t,e);if(await Ub(Xt.join(o,Kd)))return;let s=await nO(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Fs(`no ${Kd} in ${o}${a}`)}async function Qe(){return $e||(Wd||=(async()=>{try{$e=JSON.parse(await Hs.readFile(jb,"utf8"))}catch{$e={views:[],adoptWatermarks:{}}}finally{Wd=null}return $e.views||=[],$e.adoptWatermarks||={},$e})(),Wd)}async function Ce(){await zt(jb,$e)}function Ze(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,cmd:t.cmd||null,...n}}).catch(()=>{})}function Yd(t){return Us.get(t)||null}function Wb(t,{name:e,box:n}={}){let r=Us.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 Us.set(t,o),o}function Gd(t,e){let n=Us.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function rc(t,{error:e=null}={}){let n=Us.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function qb(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 Vd(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function sO(t,e){let n=Vd(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Bb.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Bb.has(s))return s}}function Ws(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=F$.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 Xd(t){if(process.platform!=="darwin"){await Oe("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Oe("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 Kb(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function aO(t,e){let n=new Set(t.views.map(o=>o.port)),r=Kb();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=Pb;o<=Nb;o++)if(!(n.has(o)||r.has(o))&&!await Ws(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Pb}-${Nb}`)}async function iO(t){try{let{stdout:e}=await Oe(B,j("list-sessions","-F","#{session_name}"));return e.split(`
|
|
136
|
-
`).some(n=>n.trim()===
|
|
106
|
+
`});function oI(t){return{id:t.id,kind:"prompt",groupId:t.groupId,provider:t.provider,box:t.box,agent:t.provider,questionTotal:t.questions.length,answeredVia:"chat",questions:t.questions.map((e,n)=>({index:n,header:e.header||"",question:e.question,multiSelect:!!e.multiSelect,chosen:[]})),ts:t.ts}}function aI(t,e,n){let r=Zw(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 mr(t){let e=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,f=new Map;for(let g of t||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let h=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let v of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let A=v[0].split("/").pop().toLowerCase();f.has(A)||f.set(A,[]),f.get(A).push(g.payload?.ts??g.ts)}}let d=3*6e4,p=20*6e4,m=(g,h)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some(A=>A>=h-d&&A<=h+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let h=It(g.payload?.text);h&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(h))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let h=It(g.payload?.text);h&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(h))}else if(g.type==="job.started"&&g.payload?.id)a.add(g.payload.id),(g.payload.kind||"agent")==="agent"&&g.payload.name&&c.add(g.payload.name);else if(g.type==="turn.user"&&g.payload?.note?.kind==="task"){let h=g.payload.note,v={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:ct(g)};h.toolUseId&&i.set(h.toolUseId,v);let A=String(h.summary||"").match(u);A&&l.set(A[1],v)}let w=new Map,y=new Map,k=null,x=new Map,_=new Map,O=new Map,z=new Map,C=new Map,P=new Map,T=new Map,D=new Map,j=new Map,it=new Map,st=new Map,E=()=>{for(let g of st.values())g.questions.length&&e.push(g);st.clear()},N=null,Y=6e4,$t=new Map,dt=(g,h)=>{let v=o.get(g);if(!v?.length)return!1;let A=It(h),tt=s.get(g);return v.some(X=>{if(!A.startsWith(X))return!1;let et=It(A.slice(X.length));return!!et&&!!tt?.has(et)})};for(let g of t||[]){let h=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length)break;{let v=h.note||Ni(h.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let A=String(v.summary||"").match(u);if(!v.toolUseId&&A&&c.has(A[1]))break}if(v.kind==="command"&&Kw(v.command))break;if(v.kind==="resume-summary"){e.push({id:g.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:ct(g),box:g.boxId,agent:g.provider});break}v.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:Vw(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:ct(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${ct(g)}`)||g.source==="transcript"&&dt(g.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:ct(g),box:g.boxId,agent:g.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(N=null);break}for(let v of x.values())v.status="stale";x.clear();for(let v of O.values())v.status="stale";O.clear(),N&&N.box===g.boxId&&(e.push(oI(N)),N=null),e.push({id:g.id,kind:"message",role:"user",text:h.text,ts:ct(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...g.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let v=h.from||{};e.push({id:g.id,kind:"event",type:"peer-message",text:h.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;E();let v=(h.images||[]).filter(et=>!m(et,ct(g)));if(!String(h.text||"").trim()&&!v.length)break;if(g.provider==="cursor"){let et=`${g.boxId}|${h.text}`;if(n.has(et))break;n.add(et)}(!N||N.box===g.boxId)&&(N=null);let A=g.provider==="codex"?(h.tools||[]).filter(et=>!Iu.has(et)&&!Os.has(et)):h.tools||[],tt=h.text,X=null;if(Du.has(g.provider)){let et=ju(String(h.text||""));et&&(tt=et.text,X={questions:et.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:tt,ts:ct(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...X?{ask:X}:{},...A.length?{tools:A}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...v.length?{images:v}:{}}),X&&(N={id:g.id,groupId:Lu(g.provider,h.sourceId||g.id,X.questions),provider:g.provider,box:g.boxId,questions:X.questions,ts:ct(g)});break}case"job.started":{if(!h.id)break;let v=ct(g),A=i.get(h.id);if(!A&&(h.kind||"agent")==="agent"&&h.name){let Ot=l.get(h.name);Ot&&Ot.ts>=v&&(A=Ot)}let tt={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:A?A.status==="killed"?"stopped":A.status&&A.status!=="completed"?"failed":"done":"running",startedTs:v,finishedTs:A?.ts??null,summary:A?.summary??null,resultHead:A?.resultHead??null},X=$t.get(g.boxId),et=X?.jobs[X.jobs.length-1]?.startedTs;if(X&&et!=null&&v-et<=Y)X.jobs.push(tt);else{let Ot={id:g.id,kind:"job-dock",dockId:h.id,ts:v,box:g.boxId,agent:g.provider,jobs:[tt]};$t.set(g.boxId,Ot),e.push(Ot)}break}case"handoff.done":e.push(ye(g,"lead-handoff"));break;case"subtask.delegated":e.push({...ye(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(ye(g,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let A of x.values())A.status="stale";x.clear();for(let A of O.values())A.status="stale";O.clear()}let v=ye(g,`pr-${h.change}`);if(v.box=g.boxId??null,sI.has(h.change)&&h.number!=null){let A=aI(w,y,h);if(w.size>=2){for(let X of y.values())for(let et of X)et.grouped=!0;v.groupPrs=[...w.values()];let tt=[v];for(let X of w.keys())y.set(X,tt)}else{let tt=y.get(A)??[];tt.push(v),y.set(A,tt)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let A=Zw(h),tt=k&&!k.complete||w.size>=2;if(w.has(A))w.delete(A),y.delete(A);else if(h.number!=null)for(let X of[...w.keys()])X.endsWith(`#${h.number}`)&&(w.delete(X),y.delete(X));if(tt&&h.number!=null){(!k||k.complete)&&(k={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:ct(g),ts:ct(g),box:g.boxId,agent:g.provider},e.push(k)),k.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:ct(g)}),k.remaining=[...w.values()],k.complete=w.size===0;break}}e.push(v);break}case"pr.offered":{if(!h.offerId)break;for(let A of x.values())A.status="stale";x.clear();for(let A of O.values())A.status="stale";O.clear();let v={id:g.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:ct(g),box:g.boxId,agent:g.provider};x.set(h.offerId,v),_.set(h.offerId,v),e.push(v);break}case"pr.offer.answered":{let v=_.get(h.offerId);v&&(v.status=h.action||"dismiss",v.actions=Array.isArray(h.actions)?h.actions:null,v.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(v.existingPr={number:h.existingPr.number??null}),x.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let A of O.values())A.status="stale";O.clear();let v={id:g.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:ct(g),box:g.boxId,agent:g.provider};O.set(h.offerId,v),z.set(h.offerId,v),e.push(v);break}case"pr.exists.answered":{let v=z.get(h.offerId);v&&(v.status=h.choice||"dismiss",O.delete(h.offerId));break}case"box.state.changed":{let v=h.change==="model"?"model-change":h.change;if(!v)break;let A=ye(g,v);v==="model-change"&&(A.to=h.model),e.push(A);break}case"track.transferred":case"track.recovered":e.push(ye(g,"track-moved"));break;case"view.state.changed":{if(h.change!=="created"||h.restarted)break;e.push({id:g.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:ct(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:ct(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(h.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:ct(g),box:g.boxId,agent:g.provider});break}if(h.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:ct(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:ct(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let A of T.values())A.box===(h.box||g.boxId)&&(A.status="stale",T.delete(A.offerId));let v={id:g.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:ct(g),box:h.box||g.boxId,agent:h.agent||g.provider};T.set(h.offerId,v),D.set(h.offerId,v),e.push(v);break}case"box.fullscreen.answered":{let v=D.get(h.offerId);v&&(v.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",T.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let A of C.values())A.key===h.key&&(A.status="stale",C.delete(A.offerId));let v={id:g.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:ct(g),box:g.boxId,agent:g.provider};C.set(h.offerId,v),P.set(h.offerId,v),e.push(v);break}case"ticket.move.answered":{let v=P.get(h.offerId);v&&(v.status=h.action==="moved"?"moved":"dismiss",C.delete(h.offerId));break}case"ticket.state.changed":{for(let v of C.values())v.key===h.key&&v.to===h.to&&(v.status="stale",C.delete(v.offerId));if(h.mode==="offer")break;e.push(ye(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(ye(g,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let A of j.values())A.status="stale";j.clear();let v={id:g.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:ct(g),box:g.boxId,agent:g.provider};j.set(h.proposalId,v),it.set(h.proposalId,v),e.push(v);break}case"ticket.create.answered":{let v=it.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,j.delete(h.proposalId));break}case"ticket.created":{let v={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},A=h.proposalId?it.get(h.proposalId):null;if(A){A.created=v;break}e.push({id:g.id,kind:"ticket-created",...v,ts:ct(g),box:g.boxId,agent:g.provider});break}case"error":{if(!h.kind)break;e.push(ye(g,h.kind));break}case"usage.cap.hit":e.push(ye(g,"usage-cap-hit"));break;case"usage.cap.warning":e.push(ye(g,"usage-cap-warning"));break;case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:h.provider||g.provider,ts:ct(g),at:ct(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(N=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let A=st.get(v);A||(A={id:g.id,kind:"prompt",groupId:v,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:h.questionTotal??1,questions:[],ts:ct(g)},st.set(v,A));let tt=h.questionIndex??0,X={index:tt,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},et=A.questions.findIndex(Ot=>Ot.index===tt&&It(Ot.question)===It(X.question));et>=0?A.questions[et]=X:A.questions.push(X),A.questions.sort((Ot,or)=>Ot.index-or.index),A.questionTotal=Math.max(A.questionTotal,h.questionTotal??1),A.questions.length>=A.questionTotal&&(e.push(A),st.delete(v));break}default:break}}return cI(e.sort((g,h)=>(g.ts??0)-(h.ts??0)))}function cI(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<=iI){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};e.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else e.delete(r.box),n.push(r)}return n}function Hu(t,e=null){let n=[];for(let o of t||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:lI(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??mr(t)).filter(o=>o.kind==="message");return Ow(n,r)}function lI(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==ao(t.command)?e:_n(t.command,t.description||"")}function ye(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:ct(t),ts:ct(t)}}function Qw(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+uI,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&dI.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}function tg(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||!pI.has(r.type)||!r.box)continue;let o=(r.ts??0)+fI,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}var ct,sI,Zw,iI,uI,dI,fI,pI,Wu=M(()=>{io();co();Ou();Bu();Uu();ct=t=>t.payload?.ts??t.ts;sI=new Set(["opened","ci-fixed","reconciled","pushed"]),Zw=t=>`${t.repo??""}#${t.number}`;iI=2*6e4;uI=12e4,dI=new Set(["archived","unarchived","opened"]);fI=2500,pI=new Set(["pr-opened","pr-merged","pr-auto-armed"])});function yI(t){let e=null,n=null,r=null;for(let o of t??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(e={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:e,lastPush:n,lastEventAt:r}}function kI(t){for(let e=(t?.length??0)-1;e>=0;e--){let n=t[e];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function xI(t,e){let n=[],r=[],o=new Set,s=new Set;for(let a=(t?.length??0)-1;a>=0;a--){let i=t[a];if(i?.type!=="tool.called")continue;if(e!=null&&(i.ts??0)<=e)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<wI&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<gI&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function og(t,{trackName:e="this Track",now:n=null,mode:r="failover"}={}){let o=yI(t),s=kI(t),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=mr(t??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-mI);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${e}`),l.push(""),l.push(rg[r]??rg.failover),l.push(""),o.lastPush?.sha){let d=ng(n,o.lastPush.ts),p=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let d=ng(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=xI(t??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${bI(d.text,hI)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
|
|
107
|
+
`);return f.length<=eg?f:`${f.slice(0,eg)}
|
|
108
|
+
|
|
109
|
+
[\u2026 brief truncated]`}var mI,hI,eg,wI,gI,bI,ng,rg,sg=M(()=>{Wu();mI=12,hI=2e3,eg=48e3,wI=30,gI=10;bI=(t,e)=>{let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1)}\u2026`},ng=(t,e)=>t&&e?Math.max(1,Math.round((t-e)/6e4)):null;rg={failover:"**This is a reconstruction, not a checkpoint.** The node this Track lived on went offline without a clean handoff; bostrat assembled this brief from the durable event log. Treat every claim below as a lead, not a fact \u2014 **verify the repository state first** (git status, git log, the diff against the last pushed ref) before continuing work.","in-place":"**This is a reconstruction, not a checkpoint.** The previous lead ended without writing one (out of credits, capped, or unresponsive); bostrat assembled this status from the Track's event log. You are in the SAME live workspace \u2014 uncommitted work is present locally. **Run git status and git log first** and treat every claim below as a lead, not a fact.",transfer:"**This Track was MOVED to a new machine while it was alive.** The previous node checkpointed and pushed its work as part of the move; this workspace was re-created from that push (uncommitted work re-applied when possible). **Run git status and git log to confirm what carried over**, then continue the objective below."}});import SI from"node:fs";import In from"node:fs/promises";import Rn from"node:path";function fg(t){let e=cr(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=ag?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,ag).toString("utf8").replace(/�+$/,"")}
|
|
110
|
+
|
|
111
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function OI(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(cg.test(s)){if(r.length>=yg)continue;let a=$I(s.replace(cg,"").replace(/[.;,:]+$/,""),bg,II);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>ig&&(o=`${o.slice(0,ig-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function kg(t,e,{timeoutMs:n=9e4}={}){let r=Rn.join(e,EI),o=Rn.join(r,_I);await In.mkdir(r,{recursive:!0});let s=await Pi(o),a=await ot(t);if(!a)throw new Error(`session "${t}" not found`);return await Bi(t,a,RI(o)),await _g(o,s,n),OI(await In.readFile(o,"utf8"))}function xg(t,e){let n=(e||"").trim();if(!n)return!1;if(t==="assistant")return n===mg||n===gg;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(pg)||n===vI||r.startsWith(hg)||r.startsWith(wg)}function Sg(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-CI;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 vg(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 Tg(t,{windows:e=[]}={}){if(xg(t.role,t.text))return!0;if(t.role!=="assistant"||!t.ts)return!1;for(let n of e)if(t.box===n.box&&t.ts>=n.from&&t.ts<=n.to)return!0;return!1}function Di(t){return qu.get(t)||null}async function Ag(t){let e=qu.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await ot(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:Rn.join(n.cwd,ug,dg)};return qu.set(t,r),PI(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function PI(t,e){t.step="summarize",await Eg(t.name,e),t.step="clear";let n=await ot(t.name);await Bi(t.name,n,"/clear"),await Vu(MI),t.step="resume",await Bi(t.name,n,AI),t.step="done",t.finishedAt=Date.now()}async function Eg(t,e,{timeoutMs:n=NI}={}){let r=Rn.join(e,ug),o=Rn.join(r,dg);await In.mkdir(r,{recursive:!0});let s=await Pi(o),a=await ot(t);await G({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:o,beforeMtime:s}),await Bi(t,a,TI(o)),await _g(o,s,n),await G({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:o,afterMtime:await Pi(o)});let i=await xw(e,o,{exclude:u=>xg(u.role,u.text)});await G({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(c){await $({id:c,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await In.readFile(o,"utf8");await $({id:c,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...fg(u),ts:Date.now()}})}catch{}}await kh(e);let l=await BI(r,o);return c&&await Oh(c,l||o,{box:t,agent:a?.agent??null}),o}async function Ku(t,{timeoutMs:e}={}){let n=await ot(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);return Eg(t,n.cwd,e?{timeoutMs:e}:void 0)}async function Gu(t,e,{trackName:n="this Track"}={}){let r=await U({id:t,cwd:e}),o=og(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Rn.join(e,lg);return await In.mkdir(Rn.dirname(s),{recursive:!0}),await In.writeFile(s,o,"utf8"),await $({id:t,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...fg(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),lg}async function Pi(t){try{return(await In.stat(t)).mtimeMs}catch{return 0}}async function _g(t,e,n){let r=Date.now()+n;for(;;){if(await Pi(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Vu(2e3)}}async function BI(t,e){try{if(!SI.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Rn.join(t,`${n}.md`);return await In.copyFile(e,r),r}catch{return null}}function Vu(t){return new Promise(e=>setTimeout(e,t))}async function Bi(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 En({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await ot(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await At(t,["Escape"],{kind:"drive-wipe"}),await Vu(150),await to(t,ki,{kind:"drive-wipe"}),await Nt(t,n,{kind:"orchestration-drive",...Re(r.agent)})}var ug,dg,ag,pg,mg,hg,vI,TI,AI,wg,gg,EI,_I,ig,yg,bg,II,RI,cg,$I,CI,MI,NI,qu,lg,zu=M(()=>{Wt();Ii();qe();Es();Sw();ii();ut();fi();sg();ug=".bostrat/handoff",dg="latest.md",ag=32*1024;pg="Checkpoint: write a work-in-progress handoff",mg="HANDOFF WRITTEN",hg="Resume from .bostrat/handoff/latest.md",vI="/clear",TI=t=>`${pg} 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 ${mg} and take no further action.`,AI=`${hg}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,wg="Beat: write a",gg="BEAT WRITTEN",EI=".bostrat/arc",_I="beat.txt",ig=200,yg=4,bg=5,II=36,RI=t=>`${wg} 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 ${yg} further lines, each starting with "- " and naming ONE piece in at most ${bg} 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 ${gg} and take no further action.`,cg=/^(?:[-–—*•·]|\d+[.)])\s+/,$I=(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};CI=6e5;MI=3500,NI=3e5,qu=new Map;lg=".bostrat/handoff/status-latest.md"});function Rg(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!DI.test(String(t)))throw new Error(`pricing.js: ${n} window ${e} must be YYYY-MM-DD, got ${JSON.stringify(t)}`);let r=Date.parse(`${t}T${e==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${e} is not a real date: ${t}`);return r}function jI(t){let e={};for(let[n,r]of Object.entries(t)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);e[n]=r.map(o=>({...o,fromMs:Rg(o.from,"from",n),toMs:Rg(o.to,"to",n)}))}return e}function FI(t){if(t==null)return Date.now();if(typeof t=="number")return Number.isFinite(t)?t:null;if(t instanceof Date){let n=t.getTime();return Number.isFinite(n)?n:null}let e=Date.parse(String(t));return Number.isFinite(e)?e:null}function UI(t){if(!t)return null;let e=String(t).trim();return Ju[e]?e:Ig[e]?Ig[e]:Object.keys(Ju).find(r=>e.startsWith(r))||null}function $g(t,e){let n=UI(t);if(!n)return null;let r=FI(e);return r===null?null:LI[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function $n(t,e={},n){let r=$g(t,n);return r?((e.input||0)*r.input+(e.output||0)*r.output+(e.cacheWrite||0)*r.cacheWrite+(e.cacheRead||0)*r.cacheRead)/1e6:0}var Ju,Ig,DI,LI,Og,Ps=M(()=>{Ju={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Ig={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},DI=/^\d{4}-\d{2}-\d{2}$/;LI=jI(Ju);Og=(t,e)=>$g(t,e)!==null});function HI(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function Mg(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function Ge(t){let e=t.payload?.model||"unknown",n=HI(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=t.source==="vendor",s=r,a=!1;if(n&&!o){let l=$n(e,n,t.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!Og(e,t.ts);return{usd:s,model:e,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function On(t,{boxLabels:e={}}={}){let n=(t||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=Cg();for(let d of n){let{usd:p,model:m,tokens:w,repriced:y,unpriced:k,asRecorded:x}=Ge(d);i+=x;let _=d.provider||"unknown",O=d.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(m)),a+=p,Mg(f,w),r[m]||={usd:0,tokens:Cg(),events:0},r[m].usd+=p,r[m].events+=1,Mg(r[m].tokens,w),o[_]||={usd:0,events:0},o[_].usd+=p,o[_].events+=1,s[O]||={usd:0,events:0,provider:_,models:new Set,label:e[O]||O},s[O].usd+=p,s[O].events+=1,m!=="unknown"&&s[O].models.add(m),d.epoch!=null){let z=(s[O].epochs||={})[d.epoch]||={usd:0,events:0,provider:_,models:new Set};z.usd+=p,z.events+=1,z.provider=_,m!=="unknown"&&z.models.add(m)}}return{totalUsd:uo(a),asRecordedUsd:uo(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:ji(r,d=>({...d,usd:uo(d.usd)})),byProvider:ji(o,d=>({...d,usd:uo(d.usd)})),byBox:ji(s,d=>({usd:uo(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:ji(d.epochs,p=>({usd:uo(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Cg,uo,ji,hr=M(()=>{Ps();Cg=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});uo=t=>Math.round(t*1e4)/1e4,ji=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]))});function WI(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 Ng(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(Yu(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Yu(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(`${Yu(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=On(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?WI(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Yu,Pg=M(()=>{hr();Yu=(t,e)=>`${t} ${e}${t===1?"":"s"}`});import{execFile as qI}from"node:child_process";import{promisify as KI}from"node:util";import{randomUUID as GI}from"node:crypto";async function Xu(t,e){let n=Date.now()+e;for(;;){if(await Qr(t))return!0;if(Date.now()>n)return!1;await Li(zI)}}async function YI(t,e){let n=Dg[e]||Dg.default;n.escape&&(await At(t,["Escape"],{kind:"swap-exit"}),await Li(150)),n.wipe&&await to(t,ki,{kind:"swap-exit"}),await xi(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Re(e).submitDelayMs||0;await Li(Math.max(r,250)),await At(t,["Enter"],{kind:"swap-exit"})}async function XI(t,e){try{let{stdout:n}=await fo(L,F("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function jg(t){await fo(L,F("respawn-pane","-k","-t",t))}async function Zu(t,e={}){if(!St(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ot(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||xn(n),a=Number(e.exitTimeoutMs)||VI;await G({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Qr(t)&&(await YI(t,o),!await Xu(t,a)&&(i=!0,await G({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await jg(t),!await Xu(t,Bg))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&lr(n),l=c?Xa():null,u=await XI(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await fo(L,F("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await fo(L,F("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?mi(y,l):y}if(f&&!i&&(await jg(t),!await Xu(t,Bg)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||GI(),m=d?n:tu(n,p,t),w=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await hn(r.cwd),s==="codex"&&await Wr(r.cwd),s==="cursor"&&await qr(r.cwd),await ur(t),await fo(L,F("set-option","-t",t,"@bostrat-sid",p)),await fo(L,F("set-option","-t",t,"@bostrat-agent",s)),await Li(JI),await Nt(t,m,{kind:"swap-launch"}),await G({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:f}}var fo,Li,VI,Bg,zI,JI,Dg,Lg=M(()=>{Wt();cs();Qa();ti();Es();qe();fo=KI(qI),Li=t=>new Promise(e=>setTimeout(e,t)),VI=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Bg=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,zI=250,JI=400,Dg={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}}});async function Fg(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Yt(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
112
|
+
`)){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?ao(p):null),detail:p?ao(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&ZI.test(QI(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)}var ZI,QI,Ug=M(()=>{ge();io();ZI=/^Async (?:agent|task) launched/i,QI=t=>{if(typeof t=="string")return t.trim();if(!Array.isArray(t))return"";let e=t.find(n=>n&&n.type==="text"&&typeof n.text=="string");return e?e.text.trim():""}});function Cn(t,e){if(!t)return null;let n=String(t);if(e){let o=e.endsWith("/")?e:`${e}/`;if(n===e)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}function tR(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 Hg(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:be(`read ${Cn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${po(r)}`:`Read ${po(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?Cn(e.path,n):null,s=e?.glob?` --glob ${Qu(e.glob)}`:"",a=be(String(r),40);return{command:be(`grep ${Qu(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=e?.glob_pattern;if(!r)return null;let o=e?.target_directory?Cn(e.target_directory,n):null;return{command:be(`glob ${Qu(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${be(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:be(`edit ${Cn(e.path,n)}`,140),description:`Edit ${po(e.path)}`}:null;case"Write":return e?.path?{command:be(`write ${Cn(e.path,n)}`,140),description:`Write ${po(e.path)}`}:null;case"Delete":return e?.path?{command:be(`delete ${Cn(e.path,n)}`,140),description:`Delete ${po(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>Cn(s,n)).filter(Boolean);return{command:be(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=tR(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:be(`apply_patch ${Cn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${po(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:be(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Fi,be,po,Qu,Wg=M(()=>{Fi=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),be=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,po=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);Qu=t=>`"${String(t).replace(/"/g,'\\"')}"`});import Bs from"node:fs";import eR from"node:fs/promises";import wr from"node:path";import nR from"node:os";import rR from"node:crypto";import oR from"node:zlib";function Gg(t){let e=rR.createHash("md5").update(String(t)).digest("hex");return wr.join(sR,e)}function Vg(t){try{let e=JSON.parse(Bs.readFileSync(wr.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 aR(t){let e=Gg(t),n;try{n=Bs.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=wr.join(e,o.name),a=wr.join(s,"store.db");if(!Bs.existsSync(a))continue;let i=Vg(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 Ui(t,e){if(!t)return null;if(e){let n=wr.join(Gg(t),e),r=wr.join(n,"store.db");if(Bs.existsSync(r))return{id:e,dir:n,store:r,...Vg(n)||{createdAtMs:null,updatedAtMs:null}}}return aR(t)}function qg(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function iR(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=oR.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function cR(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 lR(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!Fi.has(r.toolName))continue;let o=Hg(r.toolName,r.args,e);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}async function td(t,e){if(!Kg)return mo;let{DatabaseSync:n}=Kg,r;try{r=new n(t,{readOnly:!0})}catch{return mo}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 mo;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return mo}let a=s.lastUsedModel||null,i=s.latestRootBlobId?qg(r,s.latestRootBlobId):null;if(!i)return{...mo,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of cR(i)){let m=iR(qg(r,p));if(!m)continue;let w=m.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let x of m.content){let _=x?.providerOptions?.cursor?.modelName;_&&(f=_)}l.push(...lR(m.content,e).map((x,_)=>({...x,id:x.id||`${p}:${_}`}))),u.push(...dR(m.content));let y=uR(m.content);if(!y)continue;let k=[...new Set(u)];u=[],c.push({id:p,text:y,tools:k.length?k:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return mo}finally{try{r.close()}catch{}}}function zg(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(Bs.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 Jg(t,e,{limit:n=80}={}){let r=Ui(t,e);if(!r)return[];let{turns:o,model:s}=await td(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=zg(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 Yg(t,e){let n=Ui(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await td(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 Xg(t,e,{limit:n=80}={}){let r=Ui(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await td(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=zg(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:_n(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:_n(u.command,u.description),ts:i+1});return l}async function Zg(t,e){let n=Ui(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await eR.stat(n.store)).mtimeMs)}catch{return null}}var sR,uR,dR,Kg,mo,ed=M(()=>{io();Wg();sR=process.env.BOSTRAT_CURSOR_CHATS||wr.join(nR.homedir(),".cursor","chats");uR=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(`
|
|
113
|
+
`).trim():"",dR=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],Kg=process.getBuiltinModule?.("node:sqlite"),mo={turns:[],trailingCommands:[],model:null,requestIds:[]}});import fR from"node:fs/promises";function sd(t){return Qg.set(t.agent,t),t}function Ds(t){return Qg.get(t)||pR}async function id(t,e={}){return(await Ds(t.agent).readMessages(t,e)).map(ad(t))}async function ty(t,e={}){return(await Ds(t.agent).readCommands(t,e)).map(ad(t))}async function ey(t,e={}){return(await Ds(t.agent).readJobs(t,e)).map(ad(t))}var ho,nd,rd,od,pR,Qg,ad,ny=M(()=>{ge();vi();io();Ug();ed();co();ho=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}},nd=class extends ho{transcript(e){return!e?.cwd||!e?.sessionId?null:Ke(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?iu(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?$w(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?Fg(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await fR.stat(n)).mtimeMs)}catch{return null}}},rd=class extends ho{async readMessages(e,n){return Jg(e.cwd,e.sessionId,n)}async readCommands(e,n){return Xg(e.cwd,e.sessionId,n)}async updatedAt(e){return Zg(e.cwd,e.sessionId)}},od=class extends ho{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return Nw(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return Pw(e.cwd,this.sessionId(e),n)}async updatedAt(e){return Bw(e.cwd,this.sessionId(e))}},pR=new ho("unknown"),Qg=new Map;sd(new nd("claude",Au));sd(new rd("cursor",Fi));sd(new od("codex",Os));ad=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role})});function br(t){let e=t.search(dd);return(e>=0?t.slice(0,e):t).trim()}function mR(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function hR(t){let e=String(t).trim();return!!(!e||yr.test(t)||dd.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function wR(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=gR(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function gR(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 oy(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function yR(t,e=""){let n=String(t);if(!ay.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
114
|
+
`),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(gr);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&&!yr.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:br(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}function kR(t,e=""){let n=String(t).replace(/\r/g,"").split(`
|
|
115
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!iy.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(gr);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&&!(bR.test(n[c])||yr.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:br(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 xR(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
116
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(gr);l&&o.push({cursor:!!l[1],label:l[4].trim()})}let s=o.filter(c=>/^(submit answers|cancel)$/i.test(c.label));if(s.length<2)return null;let a=s.map(c=>({label:c.label,description:""})),i=Math.max(0,s.findIndex(c=>c.cursor));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}function vR(t,e=""){let n=String(t);if(!cd.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
117
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(SR.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(gr);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&&!yr.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>cd.test(u.label)))return null;let i=s.map(u=>({label:br(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}function fd(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(gr);o&&e.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=e.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}function TR(t,e=""){let n=String(t);if(!cy.test(n)||!ly.test(n)||kr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
118
|
+
`),o=fd(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:br(s.label),description:""}))}]}:null}function AR(t,e=""){let n=String(t);if(!uy.test(n)||kr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
119
|
+
`),o=fd(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:br(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}function _R(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!ER.test(o)||pd.test(t[r]))break;n+=o}return n}function IR(t,e=""){let n=String(t);if(!pd.test(n)||kr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
120
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=_R(r,s);break}return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,answerMode:"text",setup:"oauth",url:o,inputLabel:"Paste your sign-in code",promptId:"claude:setup:oauth",questions:[{question:"Open the sign-in link, approve access, then paste the code you're shown back here. The code goes straight to the Claude CLI on your box \u2014 bostrat relays the keystrokes and never stores it.",header:"Log in",multiSelect:!1,options:[]}]}:null}function $R(t,e=""){let n=String(t);if(!ld.test(n)||!RR.test(n)||kr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
121
|
+
`)].reverse().find(a=>ld.test(a))||"OAuth error").trim();return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}function OR(t,e=""){let n=String(t);if(!dy.test(n)||kr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
122
|
+
`),o=fd(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:br(s.label),description:""}))}]}:null}function CR(t,e=""){let n=String(t);if(kr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
123
|
+
`),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(fy);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(yr.test(r[l])||dd.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 MR(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(ud.test(t[r])){e=r;break}if(e<0)return null;let n=-1;for(let r=e-1;r>=0;r--)if(/[☐☒]/.test(t[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:e,optStart:n+1,optEnd:e}}function NR(t,e=""){let n=String(t);if(!sy.test(n)||!ud.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
124
|
+
`),o=MR(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let T=i;T<c;T++){let D=r[T].match(gr);D&&(u<0&&(u=T),l.push({line:T,cursor:!!D[1],checked:D[3]!=null&&D[3]!==" ",hasCheckbox:D[3]!=null,label:D[4].trim(),desc:""}))}if(!l.length)return null;for(let T of l)for(let D=T.line+1;D<Math.min(r.length,T.line+4)&&!(gr.test(r[D])||yr.test(r[D]));D++){let j=r[D].trim();if(!(!j||hR(r[D])||ry.test(j))){T.desc=mR(j);break}}let f=l.filter(T=>!ry.test(T.label));if(!f.length)return null;let d=f.some(T=>T.hasCheckbox),p=Math.max(0,f.findIndex(T=>T.cursor)),m=s>=0?s+1:0,w=[];for(let T=m;T<u;T++){let D=r[T].trim();D&&!yr.test(r[T])&&!/[☐☒]/.test(r[T])&&w.push(D)}let y=w.join(" ")||(s>=0?oy(r[s]):"")||"The agent is asking a question",k=s>=0?wR(r[s],y):null,x=k?.currentTabLabel||(s>=0?oy(r[s]):""),_=f.map(T=>({label:br(T.label),description:T.desc})),O=_.map(T=>T.label).join("|"),C=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${x}:${y}:${O}`.slice(0,240),P=k?`claude:ask:${k.tabs.map(T=>T.label).join("|")}`.slice(0,240):C;return{provider:"claude",box:e,multiSelect:d,cursorIndex:p,promptId:C,groupId:P,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:y,header:x,multiSelect:d,options:_}]}}function wo(t,e){let n=String(e||"");return n?t==="claude"?sy.test(n)&&ud.test(n)||ay.test(n)||cd.test(n)||iy.test(n.trimEnd().split(`
|
|
125
|
+
`).pop()||"")?!0:kr.test(n)?!1:cy.test(n)&&ly.test(n)||uy.test(n)||pd.test(n)||ld.test(n)||dy.test(n)||fy.test(n.trimEnd().split(`
|
|
126
|
+
`).pop()||""):t==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./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 xr(t,e){return!t||!e?null:t.agent==="claude"?xR(e,t.name)||NR(e,t.name)||yR(e,t.name)||vR(e,t.name)||$R(e,t.name)||IR(e,t.name)||AR(e,t.name)||TR(e,t.name)||OR(e,t.name)||CR(e,t.name)||kR(e,t.name):t.agent==="cursor"||t.agent==="codex"?cw(t.agent,e,t.name):null}async function md(t){if(!t)return null;let e=await Tn(t.name).catch(()=>"");return xr(t,e)}function py(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 my(t,e){let n=Number.isInteger(t?.questionIndex)?t.questionIndex:0,o=(Number.isInteger(e)?e:n)-n;if(!o)return[];let s=o>0?"Tab":"BTab";return Array.from({length:Math.abs(o)},()=>s)}var sy,ud,gr,ry,yr,dd,ay,iy,bR,cd,SR,kr,cy,ly,uy,pd,ER,ld,RR,dy,fy,hd=M(()=>{Wt();Ti();sy=/to navigate\b.*\bEsc to cancel|Enter to select\b/,ud=/Chat about this/i,gr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,ry=/^(type something|chat about this|submit)\b/i,yr=/^[─—-]{6,}\s*$/,dd=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;ay=/^\s*Switch model\?\s*$/m;iy=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,bR=/^\s*[─—▔-]{6,}\s*$/;cd=/Resume from summary/i,SR=/^\s*This session is\b/;kr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;cy=/Choose the text style that looks best with your terminal/,ly=/Let's get started\./;uy=/Select login method:/;pd=/Paste code here if prompted/,ER=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;ld=/OAuth error:/,RR=/Press Enter to retry\./;dy=/Quick safety check: Is this a project you created or one you trust/;fy=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Hi(t,e,n){if(!t||!e)return null;for(let r of PR)if(r.provider===t.agent)try{if(r.match(e,n))return r}catch{}return null}function DR(t){let e=wd.get(t);return e||(e={sentAt:[],last:0,emitted:new Set},wd.set(t,e)),e}function gy(t,e,n=Date.now()){let r=DR(t);if(r.sentAt=r.sentAt.filter(s=>n-s<wy),r.sentAt.length>=hy)return{action:"give-up"};if(n-r.last<BR)return{action:"wait"};r.last=n,r.sentAt.push(n);let o=!r.emitted.has(e);return o&&r.emitted.add(e),{action:"send",first:o}}function yy(t,e=Date.now()){let n=wd.get(t);return n?n.sentAt.filter(r=>e-r<wy).length>=hy:!1}var PR,BR,hy,wy,wd,gd=M(()=>{PR=[{provider:"claude",id:"fullscreen-upsell",match(t,e){let n=t?.questions?.[0],r=(n?.options||[]).map(s=>String(s.label||"").trim());if(r.length!==2||!/^Yes, try it$/.test(r[0])||!/^Not now$/.test(r[1]))return!1;let o=`${n?.question||""}
|
|
127
|
+
${String(e||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];BR=5e3,hy=5,wy=3600*1e3,wd=new Map});function go(t){return typeof t=="string"?t.trim():""}var by=M(()=>{});import{execFile as jR}from"node:child_process";import{promisify as LR}from"node:util";async function Tt(t,e,{input:n}={}){let r=bd("git",["-C",t,...e],{maxBuffer:ky});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Wi(t){if(!t)return!1;try{let{stdout:e}=await Tt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function xy(t){let{stdout:e}=await Tt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function FR(t){try{return await Tt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function yo(t,e="bostrat: subtask snapshot"){let{stdout:n}=await Tt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>bd("git",["-C",t,...a],{maxBuffer:ky,env:o,...i});try{let a=await FR(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 bd("rm",["-f",r])}catch{}}}async function kd(t,e){let{stdout:n}=await Tt(t,["status","--porcelain"]);return n.trim()?(await Tt(t,["add","-A"]),await Tt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function xd(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await Tt(t,["status","--porcelain"]),r=n.split(`
|
|
128
|
+
`).filter(Boolean);return e&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function Sd(t,e){if(!e)return 0;try{let{stdout:n}=await Tt(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function bo(t,e,n,r){return await Tt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Sy(t,e){try{return await Tt(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function qi(t,e,n){return await Tt(t,["worktree","add",e,n]),e}async function ko(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await Tt(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 Tt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function vd(t){await Tt(t,["init","-q"]),await Tt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function ne(t,e){try{await Tt(t,["worktree","remove","--force",e])}catch{try{await Tt(t,["worktree","prune"])}catch{}}}function UR(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 Td(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Ad(t,e,n,r){let{stdout:o}=await Tt(t,Td(["diff","--shortstat",e,n],r));return UR(o)}async function Ed(t,e,n,r){let{stdout:o}=await Tt(t,Td(["diff","--name-only",e,n],r));return o.split(`
|
|
129
|
+
`).map(s=>s.trim()).filter(Boolean)}async function _d(t,e,n,r){let{stdout:o}=await Tt(t,Td(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await yd(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await yd(t,o,["--3way","--whitespace=nowarn"]),a=await HR(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await yd(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function yd(t,e,n){try{return await Tt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function HR(t){try{let{stdout:e}=await Tt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
|
|
130
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var bd,ky,Id=M(()=>{bd=LR(jR),ky=64*1024*1024});import Rd from"node:fs/promises";import Ty from"node:path";import WR from"node:os";function Iy(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(e.getDate()-(e.getDay()+6)%7),e.getTime()}function Ry(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(1),e.getTime()}function $y(t){let e=new Date(Iy(t));return e.setDate(e.getDate()+7),e.getTime()}function Oy(t){let e=new Date(Ry(t));return e.setMonth(e.getMonth()+1),e.getTime()}function Gi(t=Date.now()){for(let[e,n]of Object.entries(xo)){let r=n.startOf(t);if(r!==Ve[e]){Ve[e]=r;for(let o of Sr.values())o[e]=0;for(let o of So)o.includes(`:${e}:`)&&!o.endsWith(`:${r}`)&&So.delete(o)}}}function Cy(t,e){let n=ke?.caps?.[t]?.[xo[e].capKey];return Number.isFinite(n)&&n>0?n:null}function vr(t,e=Date.now()){if(!t||!Od||!ke?.caps)return null;Gi(e);let n=Sr.get(t);for(let r of Object.keys(xo)){let o=Cy(t,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:t,window:r,limitUsd:o,spentUsd:Ki(s),resetsAt:xo[r].nextStart(e)}}return null}function My(t,e){let n=`hit:${t}:${e.provider}:${e.window}:${Ve[e.window]}`;return So.has(n)?!1:(So.add(n),!0)}function vy(t,e,n,r=Date.now()){if(!t||!e)return;Gi(r);let o=qR(t);for(let[s,a]of Object.entries(xo))n>=Ve[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function KR(t,e,n=Date.now()){if(!ke?.caps||!$d)return;let r=Sr.get(t);if(r)for(let[o,s]of Object.entries(xo)){let a=Cy(t,o);if(a==null)continue;let i=r[o]||0;if(i<a*_y||i>=a)continue;let c=`warn:${t}:${o}:${Ve[o]}`;if(!So.has(c)){So.add(c);try{let l=await $d(e);if(!l)continue;await $(l,{type:"usage.cap.warning",actor:"system",provider:t,payload:{provider:t,window:o,limitUsd:a,spentUsd:Ki(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function Ny(t){let e={};for(let[n,r]of Object.entries(t||{})){if(typeof n!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n))throw new Error(`bad provider key "${n}"`);let o={};for(let s of["weeklyUsd","monthlyUsd"]){let a=r?.[s];if(a==null||a==="")continue;let i=Number(a);if(!Number.isFinite(i)||i<=0)throw new Error(`${n}.${s} must be a positive number or null`);o[s]=i}Object.keys(o).length&&(e[n]=o)}return e}async function Vi(t,{source:e="local",updatedAt:n=Date.now()}={}){ke={caps:Ny(t),updatedAt:n,source:e};try{await Rd.mkdir(Ay,{recursive:!0}),await Rd.writeFile(Ey(),JSON.stringify(ke,null,2))}catch{}return js()}function js(t=Date.now()){Gi(t);let e=new Set([...Sr.keys(),...Object.keys(ke?.caps||{})]),n={};for(let r of e){let o=Sr.get(r);n[r]={weeklyUsd:Ki(o?.weekly||0),monthlyUsd:Ki(o?.monthly||0)}}return{caps:ke?.caps||{},configured:!!ke,seeded:Od,updatedAt:ke?.updatedAt??null,spent:n,windows:{weekly:{start:Ve.weekly,resetsAt:$y(t)},monthly:{start:Ve.monthly,resetsAt:Oy(t)}},warnPct:_y}}async function Py({listTracks:t,getTrack:e}={}){$d=e||null,Gi();try{let o=JSON.parse(await Rd.readFile(Ey(),"utf8"));ke={caps:Ny(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{ke=null}let n=Date.now();Xt.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Ge(o);vy(o.provider||"unknown",s,o.ts??Date.now()),KR(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(Ve.weekly,Ve.monthly);for(let o of await t?.()||[]){let s;try{s=await U(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||vy(a.provider||"unknown",Ge(a).usd,i,n)}}return Od=!0,js()}var Ay,Ey,_y,xo,ke,Od,Ve,Sr,So,$d,qR,Ki,zi=M(()=>{ut();hr();Ay=process.env.BOSTRAT_STATE_DIR||Ty.join(process.env.HOME||WR.tmpdir(),".bostrat"),Ey=()=>Ty.join(Ay,"usage-caps.json"),_y=.8;xo={weekly:{startOf:Iy,nextStart:$y,capKey:"weeklyUsd"},monthly:{startOf:Ry,nextStart:Oy,capKey:"monthlyUsd"}},ke=null,Od=!1,Ve={weekly:0,monthly:0},Sr=new Map,So=new Set,$d=null,qR=t=>{let e=Sr.get(t);return e||(e={weekly:0,monthly:0},Sr.set(t,e)),e};Ki=t=>Math.round(t*1e4)/1e4});import GR from"node:fs/promises";import VR from"node:os";import By from"node:path";function Wy(t){return t==="codex"?XR:t==="cursor"?Hy:Fy}function Dy(t){let e=String(t||"").split(`
|
|
131
|
+
`),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 jy(t){let e=String(t||"").split(`
|
|
132
|
+
`),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 ZR(t){let e=String(t||"").split(`
|
|
133
|
+
`);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 Ly(t){let e=String(t||"").split(`
|
|
134
|
+
`),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 qy(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&&!Uy.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=Hy.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!YR.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ls(t,e,{timeoutMs:n=zR}={}){let r=Date.now()+n;for(;;){let o=await xt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await Ji(JR)}}async function QR(t,e){return await Nt(t,`/model ${e.model}`,Re("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function t$(t,e){await bi(t),await Nt(t,"/model",{enter:!1}),await Ji(250),await At(t,["Enter"],{kind:"model-switch"});let n=await Ls(t,Dy);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await At(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${e.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=ZR(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await Nt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ls(t,i=>jy(i)||!Dy(i)&&o(i)&&{done:o(i)});if(!s)throw await At(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?Uy.get(e.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await At(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await Nt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ls(t,i=>!jy(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 e$(){let t=By.join(process.env.CURSOR_CONFIG_DIR||By.join(VR.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await GR.readFile(t,"utf8"))?.model||null}catch{return null}}async function n$(t,e){await bi(t),await xi(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await Ls(t,Ly),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 Ls(t,Ly,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await At(t,["Down"],{kind:"model-switch"}),await Ji(250)}if(!n||!s||!o(s)){await At(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await At(t,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${e.filter}"`:"cursor model picker didn't open")}await At(t,["Enter"],{kind:"model-switch"}),await Ji(800);let a=await e$();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 Ky(t,e,n){return t==="codex"?t$(e,n):t==="cursor"?n$(e,n):QR(e,n)}var Ji,zR,JR,Fy,YR,XR,Uy,Hy,Gy=M(()=>{Wt();Es();Ji=t=>new Promise(e=>setTimeout(e,t)),zR=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,JR=300,Fy=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 5"},{alias:"sonnet",label:"Sonnet 5"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],YR=new Set([...Fy.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),XR=[{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.6-terra",label:"GPT-5.6 Terra"},{alias:"gpt-5.6-luna",label:"GPT-5.6 Luna"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}],Uy=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Hy=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}]});function Xi(t){let e=t||{},n=r$(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function r$(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Zt(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:Zt(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:Zt(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:Zt(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered,checkpoint:t.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Zt(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:Zt(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:Zt(t.agent),payload:{...Tr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Zt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Tr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Zt(t.agent),payload:{change:"subtask-resumed",...Tr(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",...Tr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Tr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Zt(t.agent),payload:{change:"rehydrated"}};case"transferred-in":return{type:t.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Zt(t.agent),payload:{...Tr(t)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Tr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Zt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}function Jy(t,e){let n=Xi(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Md(()=>$(r,n))}async function Yy(t){if(!(!t?.id||Vy.has(t.id))){Vy.add(t.id);try{let e=await U(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=Xi(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await $(t,o)}}catch{}}}function Tr(t){let{type:e,at:n,...r}=t;return r}function Xy(t,e,n){!n?.promptId||Cd.get(t.id)===n.promptId||(Cd.set(t.id,n.promptId),Md(()=>$(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Zt(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 Nd(t,e,n,r={}){Cd.delete(t.id),Md(()=>$(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Zt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}var Zt,Yi,Md,zy,Vy,Cd,Pd=M(()=>{ut();Zt=t=>t||null;Yi=Promise.resolve(),Md=t=>(Yi=Yi.then(t).catch(()=>{}),Yi),zy=()=>Yi;Vy=new Set;Cd=new Map});function c$(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function rb(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of c$(n).reverse()){if(!o$.test(r))continue;let o=r.endsWith("?")&&r.length<=i$&&a$.test(r);if(s$.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function ob(t){let e=String(t||""),n=[];l$.test(e)&&n.push("commit"),u$.test(e)&&n.push("rebase"),d$.test(e)&&n.push("push");let r=p$.test(e);!r&&f$.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 vo(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}function sb(t,{branch:e=null,base:n=null,prNumber:r=null,offeredRebase:o=!1}={}){let s=new Set(Array.isArray(t)?t:["commit","push","pr"]),a=e?` on this branch (${e})`:"",i=[];s.has("commit")&&i.push(`commit the work${a} with a clear commit message`),s.has("rebase")&&i.push(`rebase ${s.has("commit")?"the branch":`this branch${e?` (${e})`:""}`} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`),i.push("move the work to a fresh branch (branch off the current HEAD with a clear new name)"),i.push("push the new branch to the remote"),i.push(`open a pull request${n?` against ${n}`:""} from the new branch with a concise title and a description of what changed and why (use \`gh pr create\`)`);let c=[];s.has("commit")||c.push("don't create any new commits \u2014 ship what's already committed"),o&&!s.has("rebase")&&c.push("don't rebase \u2014 leave the branch's history as it is");let l=`Yes \u2014 go ahead, as a NEW pull request: ${i.join(", then ")}.`;return c.length&&(l+=` ${c.join(", and ").replace(/^./,u=>u.toUpperCase())}.`),l+=` Leave the existing open PR${r?` #${r}`:""}${e?` and its remote branch (${e})`:" and its remote branch"} exactly as they are \u2014 don't push ${e||"the old branch"} itself.`,l}function ab(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}if(o.type==="pr.exists.answered"&&s.choice==="new"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"||o.type==="pr.exists.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&e-r.lastActivity<=m$&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function ib(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+h$||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function cb(t){let e=(Array.isArray(t)?t:[]).filter(r=>Zy[r]).map(r=>Zy[r]);return`The ship you were given the go-ahead for${e.length?` (${e.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}var Qy,tb,eb,nb,o$,s$,a$,i$,Zi,l$,u$,d$,f$,p$,m$,h$,Zy,To=M(()=>{Qy=/(?<!\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,tb=/(?<!\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,eb=/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,nb=/(?<!\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,o$=new RegExp(`\\b(?:${Qy}|${tb}|${eb}|${nb})\\b`,"i"),s$=/\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,a$=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,i$=90;Zi=["commit","rebase","push","pr"],l$=new RegExp(`\\b(?:${Qy})\\b`,"i"),u$=new RegExp(`\\b(?:${nb})\\b`,"i"),d$=new RegExp(`\\b(?:${tb})\\b`,"i"),f$=new RegExp(`\\b(?:${eb})\\b`,"i"),p$=/\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;m$=3e4;h$=2500;Zy={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import w$ from"node:fs";import Bd from"node:fs/promises";import fb from"node:path";import g$ from"node:os";function hb(t,e=!1){if(!e&&t-ub<y$)return;ub=t;let n=JSON.stringify({windowStart:Ao,byCaller:Object.fromEntries(ze),lastHour:Mn});db=db.then(()=>Bd.mkdir(pb,{recursive:!0})).then(()=>Bd.writeFile(`${Qi}.tmp`,n)).then(()=>Bd.rename(`${Qi}.tmp`,Qi)).catch(()=>{})}function wb(t){t-Ao<mb||(Mn={start:Ao,total:tc(ze),byCaller:Object.fromEntries(ze)},Mn.total&&console.log(`[github] ${Mn.total} calls last hour`,Mn.byCaller),Ao=t,ze=new Map,hb(t,!0))}function Je(t="other",e=Date.now()){wb(e),ze.set(t,(ze.get(t)||0)+1),hb(e)}function gb(t=Date.now()){return wb(t),{windowStart:Ao,total:tc(ze),byCaller:Object.fromEntries(ze),lastHour:Mn,backoffUntil:Ar>t?Ar:null}}function Nn(t){return b$.test(String(t?.message||t||""))}function Pn(t,e=Date.now()){return Ar<=e&&console.warn(`[github] rate limited \u2014 backing off ${lb/6e4} min:`,String(t?.message||t||"").slice(0,200)),Ar=Math.max(Ar,e+lb),Ar}function ec(t=Date.now()){return Math.max(0,Ar-t)}var pb,Qi,mb,lb,y$,Ao,ze,Mn,Ar,ub,tc,db,b$,Fs=M(()=>{pb=process.env.BOSTRAT_STATE_DIR||fb.join(process.env.HOME||g$.tmpdir(),".bostrat"),Qi=fb.join(pb,"github-metrics.json"),mb=60*6e4,lb=5*6e4,y$=6e4,Ao=Date.now(),ze=new Map,Mn=null,Ar=0,ub=0,tc=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(w$.readFileSync(Qi,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<mb?(Ao=t.windowStart,ze=new Map(Object.entries(t.byCaller||{})),Mn=t.lastHour||null):tc(new Map(Object.entries(t.byCaller||{})))&&(Mn={start:t.windowStart,total:tc(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}db=Promise.resolve();b$=/rate limit|too quickly|abuse detection|HTTP 429/i});import k$ from"node:crypto";import nc from"node:fs/promises";import yb from"node:path";import x$ from"node:os";async function Et(){return Bn||(Dd??=nc.readFile(Ld,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(Bn=t,!e&&kt()){let{records:n,source:r}=await Ct("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;Bn[V(i.repo,i.number)]=i}Object.keys(Bn).length&&await Fd().catch(()=>{})}}return Bn}).finally(()=>Dd=null),Dd)}async function Fd(){let t=JSON.stringify({links:Bn},null,2),e=`${Ld}.${process.pid}.tmp`;await nc.mkdir(bb,{recursive:!0}),await nc.writeFile(e,t),await nc.rename(e,Ld)}async function Pt(t){jd=jd.then(Fd,Fd),await jd.catch(()=>{});let e=Bn?.[t];e&&Ht("prlinks",{id:kb(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}async function re({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 Et(),c=V(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:S$.has(a),firstSeen:Date.now()},await Pt(V(t,e)),{link:i[c],created:!0})}async function Us(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||n.announced?null:(n.announced=!0,await Pt(V(t,e)),n)}async function qt(t,e){return!t||e==null?null:(await Et())[V(t,e)]??null}async function Dn(t,e,n){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await Pt(V(t,e)),r):null}async function xb(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await Et())[V(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Pt(V(t,e)),o):null}async function Sb(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await Et())[V(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Pt(V(t,e)),o):null}async function rc(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Pt(V(t,e)),r)}async function oc(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Pt(V(t,e)),n)}async function sc(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Pt(V(t,e)),r)}async function ac(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Pt(V(t,e)),n)}async function vb(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(r.baseFoldAt=n,await Pt(V(t,e)),r):null}async function Tb(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Pt(V(t,e)),n)}async function Ab(t,e,n){if(!t||e==null||!n)return null;let r=(await Et())[V(t,e)];return r?(r.lastHeadSha=n,await Pt(V(t,e)),r):null}async function Hs(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Pt(V(t,e)),r):null}async function Er(t,e,n=!0){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Pt(V(t,e)),r):null}async function Ws(t,e,n=!0){if(!t||e==null)return null;let r=(await Et())[V(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Pt(V(t,e)),r):null}async function Eb(t,e,n){if(!t||e==null)return null;let r=(await Et())[V(t,e)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Pt(V(t,e)),r}async function qs(t,e){if(!t||e==null)return null;let n=(await Et())[V(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Pt(V(t,e)),n)}async function _b(){return Object.values(await Et()).filter(t=>!v$.has(t.state))}async function _r(){return Object.values(await Et()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function Ib(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ct("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Et(),r=0;for(let[o,s]of Object.entries(n))await Ht("prlinks",{id:kb(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var bb,Ld,V,kb,Bn,Dd,jd,S$,v$,jn=M(()=>{kn();bb=process.env.BOSTRAT_STATE_DIR||yb.join(process.env.HOME||x$.tmpdir(),".bostrat"),Ld=yb.join(bb,"pr-links.json"),V=(t,e)=>`${t}#${Number(e)}`,kb=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=k$.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},Bn=null,Dd=null,jd=Promise.resolve();S$=new Set(["api","observed","reconciled"]);v$=new Set(["merged","closed","lost"])});function ic(t,e,n){let{state:r,reason:o}=pu(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of Ks)a-c.at>=Rb()&&Ks.delete(i);Ks.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=Ks.get(t);return s?Date.now()-s.at>=Rb()?(Ks.delete(t),!1):s.busy:!1}async function Ud(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await xt(t,{lines:Mt}).catch(()=>"");if(!ic(t,e,r))return fw(e,r)?{working:!0,prompted:!1}:n;if(wo(e,r)){let o=await Tn(t).catch(()=>r),s=xr({name:t,agent:e},o);if(s)return!Hi({name:t,agent:e},s,o)||yy(t)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function cc(t,e){return(await Ud(t,e)).working}var Rb,Ks,lc=M(()=>{Wt();Ii();hd();gd();Rb=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,Ks=new Map});import{execFile as T$}from"node:child_process";import{promisify as A$}from"node:util";async function Hd(t,e,{midTurn:n=cc,pollMs:r=E$,capMs:o=_$}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}function B$(t,e){let n=String(t||"").match(P$);return!!n&&n[1]!==e}function Wd(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Mb(t){let e=String(t||""),n=e.match(O$);if(!n)return null;let r=Wd(e,n.index);return M$.test(r)?null:{repo:r.match(Cb)?.[1]??null,head:r.match(N$)?.[1]??null}}function Nb(t){let e=String(t||""),n=e.match(C$);if(!n)return null;let r=Wd(e,n.index),o=Wd(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("-")){D$.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Cb)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function L$(t,e,{exclude:n=()=>!1}={}){let r=async i=>{Je("observer");let{stdout:c}=await $b("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",j$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Nn(l)&&Pn(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=e-I$&&!n(c)),s=await Ee({slug:t}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function F$(t,e){if(e)return[e];let n=[];for(let r of Pb(t)){let o=await lt(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await He().catch(()=>[])){let o=await lt(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Pb(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 U$(t,e,n){let r=t.workspace?.branch,o=e?.head??null,s=c=>r&&c===r||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||B$(c.headRefName,t.id),i=[];for(let c of await F$(t,e?.repo??null)){let l;try{l=await L$(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await qt(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===t.id&&f.announced)return{settled:!0};if(f&&f.trackId!==t.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==t.id||f.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function Bb(t,e,{delays:n=Ob,turnEnd:r}={}){let o=Mb(e.command);if(!o)return null;let s=!!Nb(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(Ln.has(a))return null;Ln.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await U$(t,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await re({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 Us(f,d.number))?null:(await Hd(e.box,e.agent,r),await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{Ln.delete(a)}})();return c.catch(()=>Ln.delete(a)),c}function Db(t,e,{delays:n=Ob,turnEnd:r,openedWait:o}={}){let s=Nb(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(Ln.has(a))return null;Ln.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=Pb(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await lt(w).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=t.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,w)=>{let y=await qt(c,m.number);return y&&y.trackId!==t.id?null:(y||await re({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Hs(c,m.number),w&&((await U(t).catch(()=>[])).some(_=>_.type==="pr.state.changed"&&(_.payload?.change==="auto-armed"||_.payload?.change==="merged")&&_.payload?.number===m.number)||(await Hd(e.box,e.agent,r),await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(C=>setTimeout(C,m));let w=[];for(let C of f)try{Je("observer");let{stdout:P}=await $b("gh",["pr","view",String(l),"--repo",C,"--json",H$],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Ee({slug:C})}}),T=JSON.parse(P||"null");T?.number&&w.push({slug:C,pr:T})}catch(P){Nn(P)&&Pn(P)}let y=w.find(C=>C.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let k=y.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(p=k,s.auto)return d(k,!0);continue}let x=await qt(c,k.number);if(x&&x.trackId!==t.id)return null;x||await re({repo:c,number:k.number,trackId:t.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Hd(e.box,e.agent,r);let _=await U(t).catch(()=>[]);if(_.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="merged"&&C.payload?.number===k.number))return null;let O=C=>C.some(P=>P.type==="pr.state.changed"&&P.payload?.change==="opened"&&P.payload?.number===k.number),z=0;if(!O(_)){if(Mb(e.command)&&await Us(c,k.number)){let C=Date.now();z=C+1,await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:C,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await qt(c,k.number))?.announced){let C=o?.capMs??R$,P=o?.pollMs??$$,T=Date.now()+C;for(;Date.now()<T&&(await new Promise(D=>setTimeout(D,P)),!O(await U(t).catch(()=>[]))););}}return await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),z),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await Xe(c,k.number).catch(()=>{}),await Ye(c,k.number).catch(()=>{}),await Dn(c,k.number,"merged"),k}return p?d(p,!1):null}finally{Ln.delete(a)}})();return i.catch(()=>Ln.delete(a)),i}var $b,Ob,E$,_$,I$,R$,$$,O$,C$,M$,Cb,N$,P$,D$,j$,Ln,H$,jb=M(()=>{ut();sr();zt();Fs();jn();Ir();lc();$b=A$(T$),Ob=[4e3,15e3,45e3],E$=2500,_$=5*6e4;I$=15e3,R$=9e4,$$=2e3,O$=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,C$=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,M$=/\s--web\b/,Cb=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,N$=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,P$=/^track\/.+-([0-9a-f]{8})$/;D$=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);j$="number,url,title,headRefName,createdAt";Ln=new Set;H$="number,url,title,headRefName,state"});import W$ from"node:fs/promises";import Kd from"node:path";import q$ from"node:os";function qd(t,e){let n=new RegExp(`\\b${e}="([^"]*)"`).exec(t);return n?n[1]:null}function uc(t){let e=K$.exec(String(t||""));if(!e)return null;let[,n,r]=e,o=qd(n,"from"),s=o?o.replace(/^uds:/,""):null,a=Lb(s);return{socket:s,pid:a,name:qd(n,"from-name"),mode:qd(n,"from-mode"),text:r.trim()}}function Lb(t){let e=/(\d+)\.sock$/.exec(String(t||""));return e?Number(e[1]):null}function G$(){return process.env.CLAUDE_CONFIG_DIR?Kd.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Kd.join(q$.homedir(),".claude","sessions")}async function V$(t){let e=Lb(t);if(!e)return null;try{let n=await W$.readFile(Kd.join(G$(),`${e}.json`),"utf8"),r=JSON.parse(n);return{pid:e,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function Fb(t,e){let n=await V$(t?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof e=="function")try{o=await e(n?.sessionId??null,r)??null}catch{o=null}return{socket:t?.socket??null,pid:n?.pid??t?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??t?.name??null,mode:t?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}var K$,Gd=M(()=>{K$=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});import{randomUUID as z$}from"node:crypto";function Gs(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Ub(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Hb(t){return`job|${t.box}|id:${t.id}`}async function zd(t){return Vd.has(t.id)?Vd.get(t.id):(dc.has(t.id)||dc.set(t.id,U(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(Gs({role:o,sourceId:r.payload.sourceId})),n.keys.add(Gs({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(Gs({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Gs({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(Ub({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(Hb({id:r.payload.id,box:r.boxId}));return Vd.set(t.id,n),n}).finally(()=>dc.delete(t.id))),dc.get(t.id))}async function Wb(t,e){try{let n=(await zd(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Gs(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?uc(r.text):null;if(s){await $(t,{type:"peer.message",actor:"peer",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:s.text,ts:r.ts,keyText:String(r.text||"").slice(0,40),from:await Fb(s,Vs),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}if(await $(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch){let a=rb(r.text);if(a){let{offered:i,toPr:c}=ob(a),l;if(t.workspace?.kind==="multi"){let u=await Eo(t).catch(()=>[]);l=u.length?u.map(f=>({name:f.name,slug:f.repo??null})):void 0}await $(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:z$(),ts:r.ts,excerpt:a,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:i||void 0,toPr:c||void 0,repos:l}})}}}}catch{}}async function qb(t,e){try{let n=await zd(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=Ub(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await $(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),Bb(t,o),Db(t,o))}}catch{}}async function Kb(t,e){try{let n=await zd(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Hb(o);n.keys.has(s)||(n.keys.add(s),await $(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var Vd,dc,Gb=M(()=>{ut();To();jb();Gd();Ir();Vd=new Map,dc=new Map});function J$(t){let e=[],n=null;for(let r of String(t||"").split(`
|
|
135
|
+
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;e.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return e}async function Y$(t,e){let n=`${t.id}:${e.name}`;if(!Jd.has(n)){let r=await U(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);Jd.set(n,o)}return Jd.get(n)}async function Vb(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await pr(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await Y$(t,e),o=J$(await Yt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await $(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:$n(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await $(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Jd,zb=M(()=>{ut();co();ge();Ps();Jd=new Map});import fc from"node:fs";import Xd from"node:path";import X$ from"node:os";async function tO({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(Q$,{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:Jb})});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<Jb)break}return r}function eO(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}function Xb(){try{return JSON.parse(fc.readFileSync(pc(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function rO(t){try{fc.mkdirSync(Xd.dirname(pc()),{recursive:!0});let e=pc()+".tmp";fc.writeFileSync(e,JSON.stringify(t)),fc.renameSync(e,pc())}catch{}}async function sO({fetchImpl:t,now:e=Date.now()}={}){if(Fn.rows&&e-Fn.at<nO)return Fn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Xb(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await tO({sinceMs:o,cookie:n,fetchImpl:t});Fn={at:e,rows:s.map(eO).filter(Boolean),error:null}}catch(s){Fn={at:e,rows:null,error:String(s?.message||s)}}return Fn.rows}async function aO(t,e){let n=`${t.id}:${e.name}`;if(!Yd.has(n)){let r=await U(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);Yd.set(n,o)}return Yd.get(n)}async function Zb(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Yb())return;let r=await sO({fetchImpl:n});if(!r||!r.length)return;let o=await Yg(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await aO(t,e),a=Xb(),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=$n(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await $(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}rO({sinceMs:c,seen:[...i].slice(-oO)})}catch{}}var Z$,Yb,Q$,Jb,nO,Fn,pc,oO,Zd,Yd,Qd=M(()=>{ut();ed();Ps();Z$=process.env.BOSTRAT_STATE_DIR||Xd.join(process.env.HOME||X$.tmpdir(),".bostrat"),Yb=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,Q$="https://cursor.com/api/dashboard/get-filtered-usage-events",Jb=100;nO=5*6e4,Fn={at:0,rows:null,error:null},pc=()=>Xd.join(Z$,"cursor-usage.json");oO=5e3;Zd=()=>({enabled:Yb(),lastPollAt:Fn.at||null,error:Fn.error}),Yd=new Map});import Ys from"node:fs/promises";import Qt from"node:path";import iO from"node:os";import cO from"node:net";import{randomUUID as lO}from"node:crypto";import{execFile as uO}from"node:child_process";import{promisify as dO}from"node:util";function yO(t){for(let e of af(t))if(!ok.test(e))return e;return null}function sk(t){let e=af(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function bO(t,e){let n=sk(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Qt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Qt.resolve(t,n[r+1])}return t}function xO(t){let e=!1,n=!1;for(let r of af(t)){if(!e){if(ok.test(r))continue;e=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&tf(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,tf(r))return`serves the filesystem root ("${r}")`;continue}if(tf(r))return`serves the filesystem root ("${r}")`;if(kO(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function vO(t,e){if(Qt.resolve(t)===Qt.resolve(e))return!0;let[n,r]=await Promise.all([mc(t),mc(e)]);return n===r}async function TO(t){let n=(await Ys.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 ak(Qt.join(t,o.name,rf))?`./${o.name}`:null))).filter(Boolean)}async function AO(t,e){let[n,r]=await Promise.all([mc(t),mc(e)]),o=Qt.relative(n,r);if(o&&(o.startsWith("..")||Qt.isAbsolute(o)))throw zs(`serve directory escapes the Track workspace: ${e}`)}async function EO(t,e){let n=xO(e);if(n)throw zs(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await SO(t))throw zs(`serve directory does not exist: ${t}`);let r=yO(e);if(!r)return;if(!r.includes("/")&&!await jh(r,{loginShell:!0,failOpen:!0}))throw zs(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(hO.test(e)||wO.has(r)||!mO.has(r)||!sk(e).includes("run")||gO.test(e))return;let o=bO(t,e);if(await ak(Qt.join(o,rf)))return;let s=await TO(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw zs(`no ${rf} in ${o}${a}`)}async function tn(){return $e||(ef||=(async()=>{try{$e=JSON.parse(await Ys.readFile(rk,"utf8"))}catch{$e={views:[],adoptWatermarks:{}}}finally{ef=null}return $e.views||=[],$e.adoptWatermarks||={},$e})(),ef)}async function Ce(){await Jt(rk,$e)}function Qe(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,cmd:t.cmd||null,...n}}).catch(()=>{})}function lf(t){return Js.get(t)||null}function ck(t,{name:e,box:n}={}){let r=Js.get(t);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:t,name:e||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Js.set(t,o),o}function of(t,e){let n=Js.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function hc(t,{error:e=null}={}){let n=Js.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function lk(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 sf(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function _O(t,e){let n=sf(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!ek.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!ek.has(s))return s}}function Xs(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=cO.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 uf(t){if(process.platform!=="darwin"){await Oe("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Oe("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 uk(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function IO(t,e){let n=new Set(t.views.map(o=>o.port)),r=uk();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=Qb;o<=tk;o++)if(!(n.has(o)||r.has(o))&&!await Xs(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Qb}-${tk}`)}async function RO(t){try{let{stdout:e}=await Oe(L,F("list-sessions","-F","#{session_name}"));return e.split(`
|
|
136
|
+
`).some(n=>n.trim()===_o(t))}catch{return!1}}async function Zs(t){try{await Oe(L,F("kill-session","-t",_o(t)))}catch{}}async function df(t){if(!await bs())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=_o(t.slug),n=t.serveCwd??t.cwd,r=Qt.dirname(process.execPath);await Oe(L,F("new-session","-d","-s",e,"-c",n)),await Oe(L,F("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Oe(L,F("send-keys","-t",e,"Enter")),await Oe(L,F("send-keys","-t",e,"-l",t.cmd)),await Oe(L,F("send-keys","-t",e,"Enter"))}async function nk(t,e=12){try{let{stdout:n}=await Oe(L,F("capture-pane","-t",_o(t),"-p"));return String(n||"").trimEnd().split(`
|
|
137
137
|
`).filter(Boolean).slice(-e).join(`
|
|
138
|
-
`)}catch{return""}}async function
|
|
139
|
-
`)}catch{}}function ek(){let t={};for(let[e,n]of nf)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:ac.slice(-20)}}var tk,pO,mO,ac,nf,cc=M(()=>{tk=process.env.BOSTRAT_STATE_DIR||Qb.join(process.env.HOME||fO.tmpdir(),".bostrat"),pO=Qb.join(tk,"github-latency.ndjson"),mO=100,ac=[],nf=new Map});import{execFile as wO}from"node:child_process";import{promisify as gO}from"node:util";async function yO(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([ko("git",["-C",t,"rev-parse","HEAD"]),ko("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await I({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function ie(t,e,n={}){let{caller:r="prs",...o}=n,s=await Ae({cwd:t});ze(r);try{let{stdout:a}=await ko("gh",e,{cwd:t,...bO,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Cn(a)&&Mn(a),a}}async function sf(){if(lc&&Date.now()-lc.at<6e4)return lc.data;let t;try{ze("auth");let e=await ko("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"||e?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await rs();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return lc={at:Date.now(),data:t},t}async function kO(t){try{let{stdout:e}=await ko("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function rk(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 rf(t){let e=(t||[]).map(rk),n=e.filter(s=>s.state==="pending").length,r=e.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=e.length&&!n&&r.length?Math.max(...r):null;return{total:e.length,passed:e.filter(s=>s.state==="pass").length,failed:e.filter(s=>s.state==="fail").length,pending:n,failing:e.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}function cf(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function sk(t,e){if(!e)return null;let n=await st(t);if(!n)return null;try{let r=await ie(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 ak(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 ik(t,e){let n=await sk(t,e);return n===null?{...rf([]),unavailable:!0}:rf(ak(n))}function uc(t,e=rf(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}function SO(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){bo.delete(s);continue}r.add(s),bo.has(s)||bo.set(s,n),o.mergeabilityPending=n-bo.get(s)<xO}for(let o of bo.keys())o.startsWith(`${t}#`)&&!r.has(o)&&bo.delete(o);return e}async function xo(t,e){let n=await tn(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await ie(n.path,["pr","view",String(r),"--json",af]);return uc(JSON.parse(o))}catch(o){if(!cf(o))throw o;let s=await ie(n.path,["pr","view",String(r),"--json",ok]),a=JSON.parse(s);return uc(a,await ik(n.path,a.headRefOid))}}async function Sr(t,{caller:e="prs"}={}){try{let n=await ie(t,["pr","list","--json",af,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>uc(r)).sort(nk)}catch(n){if(!cf(n))throw n;let r=await ie(t,["pr","list","--json",ok,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>uc(o,await ik(t,o.headRefOid))))).sort(nk)}}function Pe(){Ks.clear()}async function AO(t,e){if(!t)return e;for(let n of e){let r=await Wt(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 Ki(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Vi(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function ck(){let t=await sf();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Ue())await kO(a.path)&&e.push(a);let n=Date.now(),r=Wi(n)>0,o=await Promise.all(e.map(async a=>{let i=Ks.get(a.path),c=i?.pending?TO:vO;if(i&&(r||n-i.at<c))return i.entry;try{let l=await st(a.path).catch(()=>null),u=SO(a.path,await Sr(a.path,{caller:"tab"}),n);if(await AO(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&ic({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},f=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return Ks.set(a.path,{at:Date.now(),entry:d,pending:f}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Me(l)}}})),s=new Set(e.map(a=>a.path));for(let a of Ks.keys())s.has(a)||Ks.delete(a);return{auth:t,repos:o}}async function tn(t){let e=(await Ue()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function vr(t){return es(t)}async function So(t,e,{method:n="squash"}={}){let r=await tn(t);if(!Gs.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(e);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await Ae({cwd:r.path})).GH_TOKEN){let a=await Tl().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await ie(r.path,s)}catch(a){throw new Error(Me(a))}finally{Pe()}return{ok:!0,number:o}}async function Vs(t,e){let n=await tn(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ie(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Me(s))}finally{Pe()}let o=await st(n.path).catch(()=>null);return o&&await ob(o,r).catch(()=>{}),{ok:!0,number:r}}async function lf(t,e){let n=await tn(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ie(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Me(o))}finally{Pe()}return{ok:!0,number:r}}function uf(t){return t.green&&!t.isDraft}function RO(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 lk(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:uf(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function uk(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}function dk(){let t={};for(let[e,n]of of)t[e]={...n};return t}async function fk(t,{method:e="merge",onMerged:n=null}={}){let r=await tn(t);if(!Gs.has(e))throw new Error(`invalid merge method: ${e}`);if(of.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Sr(r.path)).filter(uf).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 of.set(r.path,s),OO(s,o,{listPrs:()=>Sr(r.path),merge:a=>So(r.path,a,{method:e}),sleep:IO,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function $O(t,e){let n=Date.now()+(e.settleMaxMs??_O),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(EO)}}async function OO(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 $O(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: ${Me(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(!uf(a)){t.skipped.push({number:a.number,title:a.title,reason:RO(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:Me(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Pe()}}async function df(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await ko("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Me(l)}`)}await yO(t);let o=n||"";if((await Ae({cwd:t})).GH_TOKEN){let l=await Tl().catch(()=>null);l&&(o=`${o?`${o}
|
|
138
|
+
`)}catch{return""}}async function OO(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await RO(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await nk(t.slug)||"server exited before listening"),Ze.delete(t.id),await Qe(t,t.state,t.error?{error:t.error}:{}),!0}if(await Xs(t.port))return Ze.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await Qe(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>$O?(t.state="error",t.error=await nk(t.slug)||"never started listening",await Qe(t,"error",{error:t.error}),!0):!1;let o=(Ze.get(t.id)||0)+1;return Ze.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}async function wc(){if(!nf){nf=!0;try{let t=await tn(),e=!1;for(let n of t.views)await OO(n)&&(e=!0);e&&await Ce()}finally{nf=!1}}}async function ff({trackId:t}={}){await wc();let e=await tn();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function dk(t){let n=(await tn()).views.find(r=>r.id===t);return n?{...n}:null}function pf(t){return $e&&$e.views.find(e=>e.slug===t)||null}async function mf({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=Qt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await AO(r,u),await EO(u,n)},d=await tn();if(i!=="detected"){let w=sf(e),y=String(n).trim(),k=d.views.find(x=>x.trackId===t&&(x.slug===w||sf(x.name)===w||x.cmd===y&&Qt.resolve(x.serveCwd??x.cwd)===u));if(k)return CO(d,k,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=Un)throw new Error(`view cap reached (${Un} live) \u2014 stop one first`);let m={id:lO().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:_O(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await IO(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await df(m),d.views.push(m),await Ce(),await Qe(m,"created",{origin:i}),of(t,m.id),i==="detected"&&await Xs(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await Ce(),await Qe(m,"live")),{...m}}async function CO(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 vO(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Xs(e.port))return await Ce(),of(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>=Un)throw new Error(`view cap reached (${Un} live) \u2014 stop one first`);return e.origin==="detected"?(await uf(e.port),e.origin="agent"):await Zs(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Ze.delete(e.id),await df(e),await Ce(),await Qe(e,"created",{restarted:!0,reused:!0}),of(e.trackId,e.id),{...e,reused:!0}}async function fk(t){let n=(await tn()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await uf(n.port):await Zs(n.slug),n.state!=="stopped"&&(n.state="stopped",Ze.delete(n.id),await Ce(),await Qe(n,"stopped")),{...n}}async function pk(t){let e=await tn(),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>=Un)throw new Error(`view cap reached (${Un} live) \u2014 stop one first`);return await Zs(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Ze.delete(n.id),await df(n),await Ce(),await Qe(n,"created",{restarted:!0}),{...n}}async function mk(t){let e=await tn(),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 uf(r.port):await Zs(r.slug),e.views.splice(n,1),Ze.delete(r.id),await Ce(),await Qe(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function hk(t){let e=await tn(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Zs(r.slug),Ze.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await Ce()),n.length}async function wk(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await tn(),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 Ce()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(MO)){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(!uk().has(c)&&!n.views.some(l=>l.port===c)&&await Xs(c))try{i.push(await mf({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}var Oe,fO,rk,Qb,tk,Un,pO,_o,ek,zs,mO,rf,hO,wO,gO,af,ok,tf,kO,SO,ak,mc,$e,ef,Js,cf,ik,Ze,$O,nf,MO,gc=M(()=>{Wt();ut();yn();Oe=dO(uO),fO=process.env.BOSTRAT_STATE_DIR||Qt.join(process.env.HOME||iO.tmpdir(),".bostrat"),rk=Qt.join(fO,"views.json"),[Qb,tk]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),Un=Number(process.env.BOSTRAT_VIEW_MAX)||3,pO="bostrat-view-",_o=t=>`${pO}${t}`,ek=new Set(["bostrat","www","api","views","app"]),zs=t=>Object.assign(new Error(t),{status:400}),mO=new Set(["npm","pnpm","yarn","bun"]),rf="package.json",hO=/[|;&`]|\$\(/,wO=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),gO=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,af=t=>String(t).trim().split(/\s+/).filter(Boolean),ok=/^[A-Za-z_][A-Za-z0-9_]*=/;tf=t=>t==="/",kO=t=>t===".."||t.startsWith("../")||t.includes("/../")||t.endsWith("/..");SO=t=>Ys.stat(t).then(e=>e.isDirectory(),()=>!1),ak=t=>Ys.stat(t).then(e=>e.isFile(),()=>!1),mc=t=>Ys.realpath(t).catch(()=>Qt.resolve(t));$e=null,ef=null;Js=new Map,cf=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,ik=12e3;Ze=new Map,$O=12e4;nf=!1;MO=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});import gk from"node:fs/promises";import yk from"node:path";import NO from"node:os";function bc({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};yc.push(l),yc.length>BO&&yc.shift();let u=hf.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)),hf.set(t,u),DO(l),l}async function DO(t){try{await gk.mkdir(bk,{recursive:!0}),await gk.appendFile(PO,JSON.stringify(t)+`
|
|
139
|
+
`)}catch{}}function kk(){let t={};for(let[e,n]of hf)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:yc.slice(-20)}}var bk,PO,BO,yc,hf,kc=M(()=>{bk=process.env.BOSTRAT_STATE_DIR||yk.join(process.env.HOME||NO.tmpdir(),".bostrat"),PO=yk.join(bk,"github-latency.ndjson"),BO=100,yc=[],hf=new Map});import{execFile as jO}from"node:child_process";import{promisify as LO}from"node:util";async function FO(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ro("git",["-C",t,"rev-parse","HEAD"]),Ro("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await $({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function le(t,e,n={}){let{caller:r="prs",...o}=n,s=await Ee({cwd:t});Je(r);try{let{stdout:a}=await Ro("gh",e,{cwd:t,...UO,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Nn(a)&&Pn(a),a}}async function yf(){if(xc&&Date.now()-xc.at<6e4)return xc.data;let t;try{Je("auth");let e=await Ro("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 ds();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return xc={at:Date.now(),data:t},t}async function HO(t){try{let{stdout:e}=await Ro("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function Sk(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 wf(t){let e=(t||[]).map(Sk),n=e.filter(s=>s.state==="pending").length,r=e.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=e.length&&!n&&r.length?Math.max(...r):null;return{total:e.length,passed:e.filter(s=>s.state==="pass").length,failed:e.filter(s=>s.state==="fail").length,pending:n,failing:e.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}function kf(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Tk(t,e){if(!e)return null;let n=await lt(t);if(!n)return null;try{let r=await le(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 Ak(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 Ek(t,e){let n=await Tk(t,e);return n===null?{...wf([]),unavailable:!0}:wf(Ak(n))}function Sc(t,e=wf(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}function qO(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Io.delete(s);continue}r.add(s),Io.has(s)||Io.set(s,n),o.mergeabilityPending=n-Io.get(s)<WO}for(let o of Io.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Io.delete(o);return e}async function $o(t,e){let n=await en(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await le(n.path,["pr","view",String(r),"--json",bf]);return Sc(JSON.parse(o))}catch(o){if(!kf(o))throw o;let s=await le(n.path,["pr","view",String(r),"--json",vk]),a=JSON.parse(s);return Sc(a,await Ek(n.path,a.headRefOid))}}async function Rr(t,{caller:e="prs"}={}){try{let n=await le(t,["pr","list","--json",bf,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>Sc(r)).sort(xk)}catch(n){if(!kf(n))throw n;let r=await le(t,["pr","list","--json",vk,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Sc(o,await Ek(t,o.headRefOid))))).sort(xk)}}function Ne(){Qs.clear()}async function VO(t,e){if(!t)return e;for(let n of e){let r=await qt(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 rc(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await sc(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function _k(){let t=await yf();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await He())await HO(a.path)&&e.push(a);let n=Date.now(),r=ec(n)>0,o=await Promise.all(e.map(async a=>{let i=Qs.get(a.path),c=i?.pending?GO:KO;if(i&&(r||n-i.at<c))return i.entry;try{let l=await lt(a.path).catch(()=>null),u=qO(a.path,await Rr(a.path,{caller:"tab"}),n);if(await VO(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&bc({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 Qs.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:Me(l)}}})),s=new Set(e.map(a=>a.path));for(let a of Qs.keys())s.has(a)||Qs.delete(a);return{auth:t,repos:o}}async function en(t){let e=(await He()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function $r(t){return Kr(t)}async function Oo(t,e,{method:n="squash"}={}){let r=await en(t);if(!ta.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 Ee({cwd:r.path})).GH_TOKEN){let a=await Bl().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await le(r.path,s)}catch(a){throw new Error(Me(a))}finally{Ne()}return{ok:!0,number:o}}async function ea(t,e){let n=await en(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await le(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Me(s))}finally{Ne()}let o=await lt(n.path).catch(()=>null);return o&&await vb(o,r).catch(()=>{}),{ok:!0,number:r}}async function xf(t,e){let n=await en(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await le(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Me(o))}finally{Ne()}return{ok:!0,number:r}}function Sf(t){return t.green&&!t.isDraft}function XO(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 Ik(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:Sf(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Rk(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}function $k(){let t={};for(let[e,n]of gf)t[e]={...n};return t}async function Ok(t,{method:e="merge",onMerged:n=null}={}){let r=await en(t);if(!ta.has(e))throw new Error(`invalid merge method: ${e}`);if(gf.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Rr(r.path)).filter(Sf).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 gf.set(r.path,s),QO(s,o,{listPrs:()=>Rr(r.path),merge:a=>Oo(r.path,a,{method:e}),sleep:YO,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function ZO(t,e){let n=Date.now()+(e.settleMaxMs??JO),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(zO)}}async function QO(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 ZO(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: ${Me(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(!Sf(a)){t.skipped.push({number:a.number,title:a.title,reason:XO(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:Me(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Ne()}}async function vf(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Ro("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Me(l)}`)}await FO(t);let o=n||"";if((await Ee({cwd:t})).GH_TOKEN){let l=await Bl().catch(()=>null);l&&(o=`${o?`${o}
|
|
140
140
|
|
|
141
|
-
`:""}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
|
|
141
|
+
`:""}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 le(t,s)}catch(l){throw new Error(Me(l))}finally{Ne()}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 Ck(t,e){let n=await en(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await le(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Me(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!kf(i))throw i;let c=await le(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Tk(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Me(i));o=Ak(l).map(Sk).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=tC(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await le(n.path,["run","view",c,"--log-failed"]).then(u=>eC(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function tC(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function eC(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
|
|
142
142
|
`);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
|
|
143
143
|
`+o.slice(-e).join(`
|
|
144
144
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
145
145
|
`+r.slice(-n)),r}function Me(t){return String(t?.stderr||t?.message||t||"").trim().split(`
|
|
146
|
-
`).find(n=>n.trim())||"gh failed"}var ko,bO,lc,af,ok,xO,bo,nk,vO,TO,Ks,Gs,EO,_O,IO,of,zs=M(()=>{Qn();Qt();Bn();Cs();cc();ct();ko=gO(wO),bO={timeout:3e4,maxBuffer:8*1024*1024};lc=null;af="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",ok=af.replace("statusCheckRollup,","");xO=9e4,bo=new Map;nk=(t,e)=>t.number-e.number;vO=3e4,TO=1500,Ks=new Map;Gs=new Set(["squash","merge","rebase"]);EO=2500,_O=9e4,IO=t=>new Promise(e=>setTimeout(e,t));of=new Map});function dc(t,e=Date.now()){let n=null;for(let r of t||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&e-n.ts>NO?null:n}function BO(t,e){return t?.firstSeen==null||e?.ts==null||t.firstSeen<e.ts}async function mk(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await L(t)}catch{return}let r=dc(n);if(r==null)return;let o=(t.boxes||[]).find(l=>l.name===t.leadBox);if(await Yi(t.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(ff.get(t.id)||0)<PO)return;ff.set(t.id,s);let a;try{a=await Sr(t.cwd,{caller:"reconcile"})}catch{return}let i=(a||[]).find(l=>l.headRefName===e);if(!i)return;let c=await st(t.cwd).catch(()=>null);if(c){let{link:l,created:u}=await ne({repo:c,number:i.number,trackId:t.id,url:i.url,headRef:e,title:i.title,source:"reconciled"});if(!u&&!(l?.trackId===t.id&&await Ms(c,i.number))){if(!BO(l,r))return;let d=await L(t).catch(()=>null);if(d&&dc(d)==null)return;await I(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"pushed",number:i.number,url:i.url,title:i.title,branch:e,repo:c}});return}}await I(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:i.number,url:i.url,title:i.title,branch:e,...c?{repo:c}:{}}}).catch(()=>{}),ff.delete(t.id)}var PO,ff,NO,pf=M(()=>{ct();zs();Qn();Bn();Xi();PO=6e3,ff=new Map,NO=30*6e4});import mf from"node:os";import{execFile as DO}from"node:child_process";import{promisify as jO}from"node:util";async function fc(){if(process.platform!=="darwin")return mf.freemem();try{let{stdout:t}=await LO("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:mf.freemem()}catch{return mf.freemem()}}var LO,hf=M(()=>{LO=jO(DO)});import _t from"node:fs/promises";import Rt from"node:path";import gk from"node:os";import{randomUUID as FO}from"node:crypto";import{execFile as UO}from"node:child_process";import{promisify as HO}from"node:util";import{chromium as WO}from"playwright-core";async function De(){return Tr||(wf||=(async()=>{try{Tr=JSON.parse(await _t.readFile(bk,"utf8"))}catch{Tr={mocks:[]}}finally{wf=null}return Tr.mocks||=[],Tr})(),wf)}async function ce(){await zt(bk,Tr)}function Sk(t,e){Be(t,e.v,e.videoRel?.endsWith(".webm")?"webm":"mp4"),e.posterRel&&Be(t,e.v,"png")}function qt(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...t.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function le(t,e){let n=Ys(t.cwd);return{html:Rt.join(n,`${t.id}.v${e}.html`),png:Rt.join(n,`${t.id}.v${e}.png`)}}function Js(t,e,n){if(!e)return null;let r=n==="html"?e.htmlRel:n==="png"?e.pngRel??e.posterRel??null:n==="video"?e.videoRel??null:null;return r?Rt.join(t.cwd,r):null}function ke(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 bf(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function pc(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(ZO)}function QO(t){return!Buffer.isBuffer(t)||t.length<32?null:t.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:t.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function tC(t){let e=Buffer.alloc(24),n=await _t.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function eC({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 nC(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(y<u&&(u=y),y>f&&(f=y),m<d&&(d=m),p=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:p}}},e.toString("base64"))}async function rC(t,e,{bg:n,crop:r}){let o=await t.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function oC({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await WO.launch({executablePath:KO,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new be("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:vo})).newPage();a.setDefaultTimeout(To),await a.goto(`file://${t}`,{waitUntil:"load",timeout:To}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:To});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await nC(a,i),u=l&&eC({...l,pad:zO*vo,align:vo});u&&(i=await rC(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<kk)throw new be(`capture produced a ${i.length}-byte image`);let c=bf(i);if(!c)throw new be("output is not a PNG");return await _t.writeFile(e,i),{width:Math.round(c.width/vo),height:Math.round(c.height/vo),dpr:vo,renderMs:Date.now()-r}}catch(s){throw s instanceof be?s:new be(/timeout/i.test(s?.message||"")?`render timed out after ${To}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function wk({htmlPath:t,outPath:e,width:n}){let r=await _t.mkdtemp(Rt.join(gk.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await yf(GO,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:To,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new be(c?.killed?`render timed out after ${To}ms`:"firefox exited non-zero",s)}let a=await _t.stat(e).catch(()=>null);if(!a||a.size<kk)throw new be(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=bf(await tC(e));if(!i)throw new be("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await _t.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function sC(t){if(qO==="firefox")return wk(t);try{return await oC(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return wk(t);throw e}}function vk(t){return gf=gf.then(t,t),gf}async function Tk(t,e){let n=await De(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await ce(),await qt(r,"rendering",{version:e});try{let o=await fc();if(o<JO)throw new be(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=le(r,e),a=await aC({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await _t.rm(s.png,{force:!0}).catch(()=>{});return}kf(r,{v:e,source:"html",htmlRel:Rt.relative(r.cwd,s.html),pngRel:Rt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await ce(),await qt(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),Ne(r),Be(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await ce(),await qt(r,"error",{version:e,error:r.error}),Ne(r)}}}function kf(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 iC({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?VO:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Xs({trackId:t,group:e}={}){let n=await De(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function en(t){let n=(await De()).mocks.find(r=>r.id===t);return n?{...n}:null}function Ak({html:t,image:e,video:n}){if([typeof t=="string"&&t.trim(),!!e,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(e&&!pc(e))throw new Error("image must be a PNG");if(n){let o=QO(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>hk)throw new Error(`video must be ${hk/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function Ek({trackId:t,name:e,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=iC({trackId:t,name:e,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=Ak({html:n,image:r,video:o}),m=await De(),w={id:FO().slice(0,8),trackId:t,box:c||null,agent:l||null,name:e.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:d,height:null,dpr:null,state:"queued",source:f?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await _t.mkdir(Ys(s),{recursive:!0}),m.mocks.push(w),f?(await _t.writeFile(le(w,1).html,n),await ce(),await qt(w,"created"),Ne(w),Be(w,1,"html"),vk(()=>Tk(w.id,1))):o?(await Ik(w,1,o,p),await ce(),await qt(w,"created"),await qt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),Ne(w),Sk(w,w.versions[0])):(await Rk(w,1,r),await ce(),await qt(w,"created"),await qt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),Ne(w),Be(w,1,"png")),{...w}}async function _k(t,{html:e,image:n,video:r,width:o}={}){let a=(await De()).mocks.find(u=>u.id===t);if(!a)throw new Error("mock not found");let i=typeof e=="string"&&e.trim(),c=Ak({html:e,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await _t.mkdir(Ys(a.cwd),{recursive:!0}),i?(a.state="queued",await _t.writeFile(le(a,l).html,e),await ce(),await qt(a,"updated",{version:l}),Ne(a),Be(a,l,"html"),vk(()=>Tk(a.id,l))):r?(await Ik(a,l,r,c),await ce(),await qt(a,"updated",{version:l}),await qt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:ke(a)?.durationS??null}),Ne(a),Sk(a,a.versions.find(u=>u.v===l))):(await Rk(a,l,n),await ce(),await qt(a,"updated",{version:l}),await qt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Ne(a),Be(a,l,"png")),{...a}}async function cC(t){try{let{stdout:e}=await yf(YO,["-v","error","-print_format","json","-show_format","-show_streams",t],{timeout:xk,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(e),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function lC(t,e){try{await yf(XO,["-y","-v","error","-i",t,"-frames:v","1","-f","image2",e],{timeout:xk,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await _t.stat(e).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Ik(t,e,n,r){let o=Ys(t.cwd),s=Rt.join(o,`${t.id}.v${e}.${r.ext}`),a=Rt.join(o,`${t.id}.v${e}.poster.png`);await _t.writeFile(s,n);let i=await cC(s),c=await lC(s,a);kf(t,{v:e,source:"video",htmlRel:null,pngRel:null,videoRel:Rt.relative(t.cwd,s),posterRel:c?Rt.relative(t.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(t.width=i.width)}async function Rk(t,e,n){let r=bf(n);if(!r)throw new Error("image must be a PNG");let o=le(t,e);await _t.writeFile(o.png,n),kf(t,{v:e,source:"image",htmlRel:null,pngRel:Rt.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function $k(t){let e=await De(),n=e.mocks.find(o=>o.id===t);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await qt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Ok(t){let e=await De(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=le(r,o);await _t.rm(s.html,{force:!0}).catch(()=>{}),await _t.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await _t.rm(Rt.join(r.cwd,s),{force:!0}).catch(()=>{});return await ce(),await qt(r,"deleted"),me("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Ck(t){let n=(await De()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Rt.join(yk,"mock-archive",t.id),o=Ys(t.cwd),s=Rt.join(r,".bostrat","mocks");await _t.mkdir(s,{recursive:!0});let a=await _t.readdir(o).catch(()=>[]);for(let i of a)await _t.copyFile(Rt.join(o,i),Rt.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await _t.stat(Rt.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(),Ne(i)}return await ce(),n.length}async function Mk(t){let e=await De(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await ce();for(let r of n)me("mocks",r.id).catch(()=>{})}return n.length}async function Pk(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await De();for(let r of n.mocks){await Ne(r);for(let o of r.versions||[])o.htmlRel&&await Be(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Be(r,o.v,"png"),o.videoRel&&await Be(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var yf,yk,bk,qO,KO,GO,VO,vo,zO,kk,To,JO,hk,YO,XO,xk,be,Tr,wf,Ne,Be,Ys,ZO,aC,gf,Ao=M(()=>{ct();gn();hf();bn();yf=HO(UO),yk=process.env.BOSTRAT_STATE_DIR||Rt.join(process.env.HOME||gk.tmpdir(),".bostrat"),bk=Rt.join(yk,"mocks.json"),qO=process.env.BOSTRAT_MOCK_ENGINE||"chromium",KO=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",GO=process.env.BOSTRAT_FIREFOX_BIN||"firefox",VO=1300,vo=2,zO=32,kk=1500,To=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,JO=700*1024*1024,hk=100*1024*1024,YO=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",XO=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",xk=2e4,be=class extends Error{constructor(e,n=""){super(n?`${e}
|
|
146
|
+
`).find(n=>n.trim())||"gh failed"}var Ro,UO,xc,bf,vk,WO,Io,xk,KO,GO,Qs,ta,zO,JO,YO,gf,na=M(()=>{sr();zt();jn();Fs();kc();ut();Ro=LO(jO),UO={timeout:3e4,maxBuffer:8*1024*1024};xc=null;bf="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",vk=bf.replace("statusCheckRollup,","");WO=9e4,Io=new Map;xk=(t,e)=>t.number-e.number;KO=3e4,GO=1500,Qs=new Map;ta=new Set(["squash","merge","rebase"]);zO=2500,JO=9e4,YO=t=>new Promise(e=>setTimeout(e,t));gf=new Map});function vc(t,e=Date.now()){let n=null;for(let r of t||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&e-n.ts>rC?null:n}function oC(t,e){return t?.firstSeen==null||e?.ts==null||t.firstSeen<e.ts}async function Mk(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await U(t)}catch{return}let r=vc(n);if(r==null)return;let o=(t.boxes||[]).find(l=>l.name===t.leadBox);if(await cc(t.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Tf.get(t.id)||0)<nC)return;Tf.set(t.id,s);let a;try{a=await Rr(t.cwd,{caller:"reconcile"})}catch{return}let i=(a||[]).find(l=>l.headRefName===e);if(!i)return;let c=await lt(t.cwd).catch(()=>null);if(c){let{link:l,created:u}=await re({repo:c,number:i.number,trackId:t.id,url:i.url,headRef:e,title:i.title,source:"reconciled"});if(!u&&!(l?.trackId===t.id&&await Us(c,i.number))){if(!oC(l,r))return;let f=await U(t).catch(()=>null);if(f&&vc(f)==null)return;await $(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"pushed",number:i.number,url:i.url,title:i.title,branch:e,repo:c}});return}}await $(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:i.number,url:i.url,title:i.title,branch:e,...c?{repo:c}:{}}}).catch(()=>{}),Tf.delete(t.id)}var nC,Tf,rC,Af=M(()=>{ut();na();sr();jn();lc();nC=6e3,Tf=new Map,rC=30*6e4});import Ef from"node:os";import{execFile as sC}from"node:child_process";import{promisify as aC}from"node:util";async function Tc(){if(process.platform!=="darwin")return Ef.freemem();try{let{stdout:t}=await iC("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:Ef.freemem()}catch{return Ef.freemem()}}var iC,_f=M(()=>{iC=aC(sC)});import _t from"node:fs/promises";import Rt from"node:path";import Bk from"node:os";import{randomUUID as cC}from"node:crypto";import{execFile as lC}from"node:child_process";import{promisify as uC}from"node:util";import{chromium as dC}from"playwright-core";async function De(){return Or||(If||=(async()=>{try{Or=JSON.parse(await _t.readFile(jk,"utf8"))}catch{Or={mocks:[]}}finally{If=null}return Or.mocks||=[],Or})(),If)}async function ue(){await Jt(jk,Or)}function Uk(t,e){Be(t,e.v,e.videoRel?.endsWith(".webm")?"webm":"mp4"),e.posterRel&&Be(t,e.v,"png")}function Kt(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...t.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function de(t,e){let n=oa(t.cwd);return{html:Rt.join(n,`${t.id}.v${e}.html`),png:Rt.join(n,`${t.id}.v${e}.png`)}}function ra(t,e,n){if(!e)return null;let r=n==="html"?e.htmlRel:n==="png"?e.pngRel??e.posterRel??null:n==="video"?e.videoRel??null:null;return r?Rt.join(t.cwd,r):null}function Se(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 Of(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function Ac(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(kC)}function xC(t){return!Buffer.isBuffer(t)||t.length<32?null:t.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:t.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function SC(t){let e=Buffer.alloc(24),n=await _t.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function vC({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 TC(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(y<u&&(u=y),y>d&&(d=y),m<f&&(f=m),p=m);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},e.toString("base64"))}async function AC(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 EC({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await dC.launch({executablePath:pC,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new xe("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Co})).newPage();a.setDefaultTimeout(Mo),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Mo}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Mo});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await TC(a,i),u=l&&vC({...l,pad:wC*Co,align:Co});u&&(i=await AC(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Lk)throw new xe(`capture produced a ${i.length}-byte image`);let c=Of(i);if(!c)throw new xe("output is not a PNG");return await _t.writeFile(e,i),{width:Math.round(c.width/Co),height:Math.round(c.height/Co),dpr:Co,renderMs:Date.now()-r}}catch(s){throw s instanceof xe?s:new xe(/timeout/i.test(s?.message||"")?`render timed out after ${Mo}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function Pk({htmlPath:t,outPath:e,width:n}){let r=await _t.mkdtemp(Rt.join(Bk.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await $f(mC,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Mo,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new xe(c?.killed?`render timed out after ${Mo}ms`:"firefox exited non-zero",s)}let a=await _t.stat(e).catch(()=>null);if(!a||a.size<Lk)throw new xe(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Of(await SC(e));if(!i)throw new xe("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await _t.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function _C(t){if(fC==="firefox")return Pk(t);try{return await EC(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return Pk(t);throw e}}function Hk(t){return Rf=Rf.then(t,t),Rf}async function Wk(t,e){let n=await De(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await ue(),await Kt(r,"rendering",{version:e});try{let o=await Tc();if(o<gC)throw new xe(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=de(r,e),a=await IC({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await _t.rm(s.png,{force:!0}).catch(()=>{});return}Cf(r,{v:e,source:"html",htmlRel:Rt.relative(r.cwd,s.html),pngRel:Rt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await ue(),await Kt(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),Pe(r),Be(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await ue(),await Kt(r,"error",{version:e,error:r.error}),Pe(r)}}}function Cf(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 RC({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?hC:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function sa({trackId:t,group:e}={}){let n=await De(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function nn(t){let n=(await De()).mocks.find(r=>r.id===t);return n?{...n}:null}function qk({html:t,image:e,video:n}){if([typeof t=="string"&&t.trim(),!!e,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(e&&!Ac(e))throw new Error("image must be a PNG");if(n){let o=xC(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>Nk)throw new Error(`video must be ${Nk/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function Kk({trackId:t,name:e,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=RC({trackId:t,name:e,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=qk({html:n,image:r,video:o}),m=await De(),w={id:cC().slice(0,8),trackId:t,box:c||null,agent:l||null,name:e.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:f,height:null,dpr:null,state:"queued",source:d?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await _t.mkdir(oa(s),{recursive:!0}),m.mocks.push(w),d?(await _t.writeFile(de(w,1).html,n),await ue(),await Kt(w,"created"),Pe(w),Be(w,1,"html"),Hk(()=>Wk(w.id,1))):o?(await Vk(w,1,o,p),await ue(),await Kt(w,"created"),await Kt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),Pe(w),Uk(w,w.versions[0])):(await zk(w,1,r),await ue(),await Kt(w,"created"),await Kt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),Pe(w),Be(w,1,"png")),{...w}}async function Gk(t,{html:e,image:n,video:r,width:o}={}){let a=(await De()).mocks.find(u=>u.id===t);if(!a)throw new Error("mock not found");let i=typeof e=="string"&&e.trim(),c=qk({html:e,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await _t.mkdir(oa(a.cwd),{recursive:!0}),i?(a.state="queued",await _t.writeFile(de(a,l).html,e),await ue(),await Kt(a,"updated",{version:l}),Pe(a),Be(a,l,"html"),Hk(()=>Wk(a.id,l))):r?(await Vk(a,l,r,c),await ue(),await Kt(a,"updated",{version:l}),await Kt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:Se(a)?.durationS??null}),Pe(a),Uk(a,a.versions.find(u=>u.v===l))):(await zk(a,l,n),await ue(),await Kt(a,"updated",{version:l}),await Kt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Pe(a),Be(a,l,"png")),{...a}}async function $C(t){try{let{stdout:e}=await $f(yC,["-v","error","-print_format","json","-show_format","-show_streams",t],{timeout:Fk,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(e),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function OC(t,e){try{await $f(bC,["-y","-v","error","-i",t,"-frames:v","1","-f","image2",e],{timeout:Fk,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await _t.stat(e).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Vk(t,e,n,r){let o=oa(t.cwd),s=Rt.join(o,`${t.id}.v${e}.${r.ext}`),a=Rt.join(o,`${t.id}.v${e}.poster.png`);await _t.writeFile(s,n);let i=await $C(s),c=await OC(s,a);Cf(t,{v:e,source:"video",htmlRel:null,pngRel:null,videoRel:Rt.relative(t.cwd,s),posterRel:c?Rt.relative(t.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(t.width=i.width)}async function zk(t,e,n){let r=Of(n);if(!r)throw new Error("image must be a PNG");let o=de(t,e);await _t.writeFile(o.png,n),Cf(t,{v:e,source:"image",htmlRel:null,pngRel:Rt.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Jk(t){let e=await De(),n=e.mocks.find(o=>o.id===t);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Kt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Yk(t){let e=await De(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=de(r,o);await _t.rm(s.html,{force:!0}).catch(()=>{}),await _t.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await _t.rm(Rt.join(r.cwd,s),{force:!0}).catch(()=>{});return await ue(),await Kt(r,"deleted"),he("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Xk(t){let n=(await De()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Rt.join(Dk,"mock-archive",t.id),o=oa(t.cwd),s=Rt.join(r,".bostrat","mocks");await _t.mkdir(s,{recursive:!0});let a=await _t.readdir(o).catch(()=>[]);for(let i of a)await _t.copyFile(Rt.join(o,i),Rt.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await _t.stat(Rt.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=t.name||t.id,i.updatedAt=Date.now(),Pe(i)}return await ue(),n.length}async function Zk(t){let e=await De(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await ue();for(let r of n)he("mocks",r.id).catch(()=>{})}return n.length}async function Qk(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ct("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await De();for(let r of n.mocks){await Pe(r);for(let o of r.versions||[])o.htmlRel&&await Be(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Be(r,o.v,"png"),o.videoRel&&await Be(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var $f,Dk,jk,fC,pC,mC,hC,Co,wC,Lk,Mo,gC,Nk,yC,bC,Fk,xe,Or,If,Pe,Be,oa,kC,IC,Rf,No=M(()=>{ut();yn();_f();kn();$f=uC(lC),Dk=process.env.BOSTRAT_STATE_DIR||Rt.join(process.env.HOME||Bk.tmpdir(),".bostrat"),jk=Rt.join(Dk,"mocks.json"),fC=process.env.BOSTRAT_MOCK_ENGINE||"chromium",pC=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",mC=process.env.BOSTRAT_FIREFOX_BIN||"firefox",hC=1300,Co=2,wC=32,Lk=1500,Mo=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,gC=700*1024*1024,Nk=100*1024*1024,yC=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",bC=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",Fk=2e4,xe=class extends Error{constructor(e,n=""){super(n?`${e}
|
|
147
147
|
${n.trim().split(`
|
|
148
148
|
`).slice(-6).join(`
|
|
149
|
-
`)}`:e),this.name="MockRenderError"}},Tr=null,wf=null;Ne=t=>Ut("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Be=(t,e,n)=>{let r=(t.versions||[]).find(s=>s.v===e),o=n==="mp4"||n==="webm"?Js(t,r,"video"):r&&Js(t,r,n)||(n==="html"?le(t,e).html:null);return o?yn("mocks",t.id,e,n,o).catch(()=>{}):Promise.resolve()};Ys=t=>Rt.join(t,".bostrat","mocks");ZO=Buffer.from([137,80,78,71,13,10,26,10]);aC=sC,gf=Promise.resolve()});import uC from"node:fs/promises";import Lk from"node:path";import dC from"node:os";import{randomUUID as Fk}from"node:crypto";async function rn(){return nn||(xf||=(async()=>{let t=!0;try{nn=JSON.parse(await uC.readFile(Uk,"utf8"))}catch{nn={flows:[]},t=!1}finally{xf=null}if(nn.flows||=[],!t&&wt()){let{records:e,source:n}=await $t("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&nn.flows.push(r.data);nn.flows.length&&await Eo().catch(()=>{})}}return nn})(),xf)}async function Eo(){await zt(Uk,nn)}function Sf(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Hk({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>Nk)throw new Error(`steps must be an array of 1-${Nk}`);let r=await Xs({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>Dk)throw new Error(`${l}: caption over ${Dk} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>jk)throw new Error(`${l}: title over ${jk} chars`);let f=String(c.mock??"").trim().replace(/^mock:/,"");if(!f)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===f);if(p.length||(p=r.filter(S=>S.name.toLowerCase()===f.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${f}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${f}" is ambiguous (${p.map(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let m=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(S=>S.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(S=>S.mockId===m.id),k=y>=0?o.splice(y,1)[0].id:Fk().slice(0,8);s.push({id:k,mockId:m.id,caption:u,title:d,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function pC(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Bk)throw new Error(`name over ${Bk} chars`);return t.trim()}async function Wk({trackId:t}={}){let e=await rn();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function mC(t){let n=(await rn()).flows.find(r=>r.id===t);return n?{...n}:null}async function qk(t){let e=await mC(t);if(!e)return null;let n=new Map((await Xs({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=ke(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 Kk({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=pC(e),i=await rn(),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 Hk({trackId:t,steps:n}),d=Date.now(),f={id:Fk().slice(0,8),trackId:t,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:d}],steps:l,createdAt:d,updatedAt:d};return i.flows.push(f),await Eo(),await Sf(f,"created"),mc(f),{...f}}async function Gk(t,{steps:e,note:n}={}){let o=(await rn()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Hk({trackId:o.trackId,steps:e,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await Eo(),await Sf(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),mc(o),{...o}}async function Vk(t){let e=await rn(),n=e.flows.findIndex(o=>o.id===t);if(n===-1)throw new Error("flow not found");let r=e.flows[n];return e.flows.splice(n,1),await Eo(),await Sf(r,"deleted"),me("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function zk(t){return(await rn()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function Jk(t){let n=(await rn()).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(),mc(r);return n.length&&await Eo(),n.length}async function Yk(t){let e=await rn(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await Eo();for(let r of n)me("flows",r.id).catch(()=>{})}return n.length}async function Xk(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await rn();for(let r of n.flows)await mc(r);return{pushed:n.flows.length,source:"plane"}}var fC,Uk,Nk,Bk,Dk,jk,nn,xf,mc,vf=M(()=>{ct();gn();Ao();bn();fC=process.env.BOSTRAT_STATE_DIR||Lk.join(process.env.HOME||dC.tmpdir(),".bostrat"),Uk=Lk.join(fC,"flows.json"),Nk=20,Bk=80,Dk=500,jk=80,nn=null,xf=null;mc=t=>Ut("flows",{...t},{trackId:t.trackId}).catch(()=>{})});import an from"node:fs/promises";import cn from"node:path";import hC from"node:os";import{randomUUID as wC}from"node:crypto";function Zs(t){return gC.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}async function je(){return on||(Tf||=(async()=>{let t=!0;try{on=JSON.parse(await an.readFile(Qk,"utf8"))}catch{on={runbooks:[]},t=!1}finally{Tf=null}return on.runbooks||=[],!t&&wt()&&await yC().catch(()=>{}),on})(),Tf)}async function Er(){await zt(Qk,on)}function wc(t){return Qs(Ut("runbooks",{...t},{trackId:t.trackId}))}async function yC(){let{records:t,source:e}=await $t("runbooks");if(!(e!=="plane"||!t.length)){await an.mkdir(hc,{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 ls("runbooks",r.id,o.v,"md");typeof s=="string"&&await an.writeFile(sn(r,o.v),s).catch(()=>{})}on.runbooks.push(r)}on.runbooks.length&&await Er()}}function _o(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function sn(t,e){return cn.join(hc,`${t.id}.v${e}.md`)}function t0(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 e0(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>Zk)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${Zk/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Zs(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 bC(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function kC({trackId:t,status:e}={}){let n=await je(),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 n0({trackId:t,status:e}={}){let n=await je(),r=await kC({trackId:t,status:e});if(!wt())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await $t("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!t||c.trackId===t)&&(!e||c.status===e));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Af(t){let e=await xC(t);if(e)return e;if(!wt())return null;let{records:n,source:r}=await $t("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===t);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function r0(t,e){let n=await ls("runbooks",t.id,e,"md");return typeof n=="string"?n:null}async function xC(t){let n=(await je()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function o0({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=e0(r),c=await je(),l={id:wC().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 an.mkdir(hc,{recursive:!0}),await an.writeFile(sn(l,1),r),l.versions.push({v:1,mdRel:cn.relative(Ar,sn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Er(),await _o(l,"created",{version:1}),wc(l),Qs(yn("runbooks",l.id,1,"md",sn(l,1))),{...l}}async function s0(t,{md:e,note:n}={}){let o=(await je()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");bC(o,"update");let s=e0(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await an.mkdir(hc,{recursive:!0}),await an.writeFile(sn(o,a),e),o.versions.push({v:a,mdRel:cn.relative(Ar,sn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Er(),await _o(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),wc(o),Qs(yn("runbooks",o.id,a,"md",sn(o,a))),{...o}}async function a0(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await je()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Er(),await _o(r,e==="done"?"done":"reopened"),wc(r),{...r})}async function i0(t){let e=await je(),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 an.rm(cn.join(Ar,o.mdRel),{force:!0}).catch(()=>{});return await Er(),r.trackDeletedAt||await _o(r,"deleted"),Qs(me("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function c0(t){let n=(await je()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await _o(r,"orphaned"),r.cwd=null,wc(r);return n.length&&await Er(),n.length}async function l0(t){let e=await je(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await _o(r,"deleted");for(let o of r.versions||[])await an.rm(cn.join(Ar,o.mdRel),{force:!0}).catch(()=>{});Qs(me("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Er()),n.length}async function u0(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await $t("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await je();for(let r of n.runbooks){await Ut("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await yn("runbooks",r.id,o.v,"md",cn.join(Ar,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Ar,Qk,hc,Zk,gC,on,Tf,Qs,gc=M(()=>{gn();ct();bn();Ar=process.env.BOSTRAT_STATE_DIR||cn.join(process.env.HOME||hC.tmpdir(),".bostrat"),Qk=cn.join(Ar,"runbooks.json"),hc=cn.join(Ar,"runbooks"),Zk=256*1024,gC=[{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/}];on=null,Tf=null;Qs=t=>Promise.resolve(t).catch(()=>{})});import Ef from"node:fs/promises";import ta from"node:path";import{randomUUID as SC}from"node:crypto";function If(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return vC.has(e)?"image":TC.has(e)?"doc":null}function AC(t){let r=ta.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 p0(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>d0)throw new Error(`file too large \u2014 max ${Math.floor(d0/(1024*1024))} MB`);let r=AC(e),o=ta.extname(r).slice(1),s=If(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=SC().slice(0,8),i=_f(t);await Ef.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Ef.writeFile(ta.join(i,c),n,{mode:384}),await Ef.chmod(ta.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${f0}/${c}`,kind:s,size:n.length}}function m0(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}function Rf(t){return t.role==="user"&&EC.test((t.text||"").trim())}var f0,_f,d0,vC,TC,EC,$f=M(()=>{f0=".bostrat/uploads",_f=t=>ta.join(t,f0),d0=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,vC=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),TC=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"]);EC=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function _r(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var h0=M(()=>{});var J0={};gl(J0,{HANDOFF_LATEST_REL:()=>fa,KNOWN_AGENTS:()=>ua,addTrackRepo:()=>Zf,agentAvailability:()=>xc,answerFullscreenOffer:()=>sp,answerPrompt:()=>Tc,archiveTrack:()=>Rr,archiveTrackForMergedPr:()=>Je,augmentFirstPromptAfterHandoff:()=>S0,buildAgentCmd:()=>x0,canResumeSubtask:()=>G0,cancelQueued:()=>pa,cancelQueuedToDraft:()=>Vf,cancelStopJob:()=>ma,cancelSubtask:()=>Mc,changedRepos:()=>go,createSetupTrack:()=>ep,createTrack:()=>_c,deleteTrack:()=>np,ensureLeadEpochs:()=>ap,forceMergeSubtask:()=>dp,getHandoffJob:()=>No,getSubtaskJob:()=>_M,getTrack:()=>z,interruptLead:()=>Ac,isArchiving:()=>kM,isHandoffNoteEcho:()=>jf,isInterruptEcho:()=>Lf,isModelSwitchEcho:()=>Ff,listSubtaskJobs:()=>$r,listTracks:()=>et,matchTranscriptsToBoxes:()=>A0,mergeNotifyMessage:()=>U0,navigatePrompt:()=>Kf,notifyTrackForMergedPr:()=>Ye,notifyTrackForResolvedPr:()=>Ic,queueShipCompleteNotify:()=>zf,queuedMessages:()=>Co,reapArchivedOrphans:()=>op,reconcileHandoffOutcome:()=>W0,reconcileInterruptedHandoffs:()=>ip,reconcileInterruptedSubtasks:()=>cp,reconcileSubtaskOutcome:()=>K0,refreshStandingInstructions:()=>tp,rehydrateLead:()=>Rc,removeTrackRepo:()=>Qf,reposOf:()=>pe,resolveTrackBox:()=>Ls,resolveTrackBoxByHookToken:()=>Wf,restartLead:()=>$c,resumeAgentCmd:()=>Hf,sayToLead:()=>fe,sendOptionsForAgent:()=>Ie,setTrackDraft:()=>Yf,setTrackModel:()=>Xf,startLeadHandoff:()=>Oc,startSpinUpView:()=>Jf,startSubtask:()=>up,stopJobActive:()=>$0,subtaskMessages:()=>qf,trackMessages:()=>vc,unarchiveTrack:()=>rp});import U from"node:fs/promises";import H from"node:path";import{randomUUID as la}from"node:crypto";import{execFile as _C}from"node:child_process";import{promisify as IC}from"node:util";function x0(t,{skipPermissions:e=!0}={}){let n=kc[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 Hf(t,e,{skipPermissions:n=!0}={}){let r=kc[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 da(t){let e=kc[t];if(e)return process.env[e.envVar]||e.base}async function xc({maxAgeMs:t=6e4}={}){if(Fn.value&&Date.now()-Fn.at<t)return Fn.value;let e=Fn.value,n=await Promise.all(ua.map(async r=>{let o=await oi(da(r),{retries:[]});return o===Wr?[r,e?.[r]??!0]:[r,o===ri]}));return Fn.value=Object.fromEntries(n),Fn.at=Date.now(),Fn.value}async function Sc(t,e=!0){let n=x0(t,{skipPermissions:e});if(await oi(n)===Hr){Fn.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.":Kl[r]?`Install it on this node: ${Kl[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). ${o}`)}return n}function S0(t,e=fa){return`${t.trim()}
|
|
149
|
+
`)}`:e),this.name="MockRenderError"}},Or=null,If=null;Pe=t=>Ht("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Be=(t,e,n)=>{let r=(t.versions||[]).find(s=>s.v===e),o=n==="mp4"||n==="webm"?ra(t,r,"video"):r&&ra(t,r,n)||(n==="html"?de(t,e).html:null);return o?bn("mocks",t.id,e,n,o).catch(()=>{}):Promise.resolve()};oa=t=>Rt.join(t,".bostrat","mocks");kC=Buffer.from([137,80,78,71,13,10,26,10]);IC=_C,Rf=Promise.resolve()});import CC from"node:fs/promises";import o0 from"node:path";import MC from"node:os";import{randomUUID as s0}from"node:crypto";async function on(){return rn||(Mf||=(async()=>{let t=!0;try{rn=JSON.parse(await CC.readFile(a0,"utf8"))}catch{rn={flows:[]},t=!1}finally{Mf=null}if(rn.flows||=[],!t&&kt()){let{records:e,source:n}=await Ct("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&rn.flows.push(r.data);rn.flows.length&&await Po().catch(()=>{})}}return rn})(),Mf)}async function Po(){await Jt(a0,rn)}function Nf(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function i0({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>t0)throw new Error(`steps must be an array of 1-${t0}`);let r=await sa({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>n0)throw new Error(`${l}: caption over ${n0} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>r0)throw new Error(`${l}: title over ${r0} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(x=>x.id===d);if(p.length||(p=r.filter(x=>x.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(x=>`mock:${x.id}`).join(", ")}) \u2014 use the id`);let m=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(x=>x.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(x=>x.mockId===m.id),k=y>=0?o.splice(y,1)[0].id:s0().slice(0,8);s.push({id:k,mockId:m.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function PC(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>e0)throw new Error(`name over ${e0} chars`);return t.trim()}async function c0({trackId:t}={}){let e=await on();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function BC(t){let n=(await on()).flows.find(r=>r.id===t);return n?{...n}:null}async function l0(t){let e=await BC(t);if(!e)return null;let n=new Map((await sa({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=Se(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 u0({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=PC(e),i=await on(),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 i0({trackId:t,steps:n}),f=Date.now(),d={id:s0().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 Po(),await Nf(d,"created"),Ec(d),{...d}}async function d0(t,{steps:e,note:n}={}){let o=(await on()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await i0({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 Po(),await Nf(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Ec(o),{...o}}async function f0(t){let e=await on(),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 Po(),await Nf(r,"deleted"),he("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function p0(t){return(await on()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function m0(t){let n=(await on()).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(),Ec(r);return n.length&&await Po(),n.length}async function h0(t){let e=await on(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await Po();for(let r of n)he("flows",r.id).catch(()=>{})}return n.length}async function w0(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ct("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await on();for(let r of n.flows)await Ec(r);return{pushed:n.flows.length,source:"plane"}}var NC,a0,t0,e0,n0,r0,rn,Mf,Ec,Pf=M(()=>{ut();yn();No();kn();NC=process.env.BOSTRAT_STATE_DIR||o0.join(process.env.HOME||MC.tmpdir(),".bostrat"),a0=o0.join(NC,"flows.json"),t0=20,e0=80,n0=500,r0=80,rn=null,Mf=null;Ec=t=>Ht("flows",{...t},{trackId:t.trackId}).catch(()=>{})});import cn from"node:fs/promises";import ln from"node:path";import DC from"node:os";import{randomUUID as jC}from"node:crypto";function aa(t){return LC.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}async function je(){return sn||(Bf||=(async()=>{let t=!0;try{sn=JSON.parse(await cn.readFile(y0,"utf8"))}catch{sn={runbooks:[]},t=!1}finally{Bf=null}return sn.runbooks||=[],!t&&kt()&&await FC().catch(()=>{}),sn})(),Bf)}async function Mr(){await Jt(y0,sn)}function Ic(t){return ia(Ht("runbooks",{...t},{trackId:t.trackId}))}async function FC(){let{records:t,source:e}=await Ct("runbooks");if(!(e!=="plane"||!t.length)){await cn.mkdir(_c,{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 gs("runbooks",r.id,o.v,"md");typeof s=="string"&&await cn.writeFile(an(r,o.v),s).catch(()=>{})}sn.runbooks.push(r)}sn.runbooks.length&&await Mr()}}function Bo(t,e,n={}){return $({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function an(t,e){return ln.join(_c,`${t.id}.v${e}.md`)}function b0(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 k0(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>g0)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${g0/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=aa(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 UC(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function HC({trackId:t,status:e}={}){let n=await je(),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 x0({trackId:t,status:e}={}){let n=await je(),r=await HC({trackId:t,status:e});if(!kt())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Ct("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!t||c.trackId===t)&&(!e||c.status===e));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Df(t){let e=await WC(t);if(e)return e;if(!kt())return null;let{records:n,source:r}=await Ct("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===t);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function S0(t,e){let n=await gs("runbooks",t.id,e,"md");return typeof n=="string"?n:null}async function WC(t){let n=(await je()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function v0({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=k0(r),c=await je(),l={id:jC().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 cn.mkdir(_c,{recursive:!0}),await cn.writeFile(an(l,1),r),l.versions.push({v:1,mdRel:ln.relative(Cr,an(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Mr(),await Bo(l,"created",{version:1}),Ic(l),ia(bn("runbooks",l.id,1,"md",an(l,1))),{...l}}async function T0(t,{md:e,note:n}={}){let o=(await je()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");UC(o,"update");let s=k0(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await cn.mkdir(_c,{recursive:!0}),await cn.writeFile(an(o,a),e),o.versions.push({v:a,mdRel:ln.relative(Cr,an(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Mr(),await Bo(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Ic(o),ia(bn("runbooks",o.id,a,"md",an(o,a))),{...o}}async function A0(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await je()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Mr(),await Bo(r,e==="done"?"done":"reopened"),Ic(r),{...r})}async function E0(t){let e=await je(),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 cn.rm(ln.join(Cr,o.mdRel),{force:!0}).catch(()=>{});return await Mr(),r.trackDeletedAt||await Bo(r,"deleted"),ia(he("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function _0(t){let n=(await je()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Bo(r,"orphaned"),r.cwd=null,Ic(r);return n.length&&await Mr(),n.length}async function I0(t){let e=await je(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Bo(r,"deleted");for(let o of r.versions||[])await cn.rm(ln.join(Cr,o.mdRel),{force:!0}).catch(()=>{});ia(he("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Mr()),n.length}async function R0(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ct("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await je();for(let r of n.runbooks){await Ht("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await bn("runbooks",r.id,o.v,"md",ln.join(Cr,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Cr,y0,_c,g0,LC,sn,Bf,ia,Rc=M(()=>{yn();ut();kn();Cr=process.env.BOSTRAT_STATE_DIR||ln.join(process.env.HOME||DC.tmpdir(),".bostrat"),y0=ln.join(Cr,"runbooks.json"),_c=ln.join(Cr,"runbooks"),g0=256*1024,LC=[{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/}];sn=null,Bf=null;ia=t=>Promise.resolve(t).catch(()=>{})});import jf from"node:fs/promises";import ca from"node:path";import{randomUUID as qC}from"node:crypto";function Ff(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return KC.has(e)?"image":GC.has(e)?"doc":null}function VC(t){let r=ca.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 C0(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>$0)throw new Error(`file too large \u2014 max ${Math.floor($0/(1024*1024))} MB`);let r=VC(e),o=ca.extname(r).slice(1),s=Ff(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=qC().slice(0,8),i=Lf(t);await jf.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await jf.writeFile(ca.join(i,c),n,{mode:384}),await jf.chmod(ca.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${O0}/${c}`,kind:s,size:n.length}}function M0(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}function Uf(t){return t.role==="user"&&zC.test((t.text||"").trim())}var O0,Lf,$0,KC,GC,zC,Hf=M(()=>{O0=".bostrat/uploads",Lf=t=>ca.join(t,O0),$0=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,KC=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),GC=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"]);zC=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Nr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var N0=M(()=>{});import{execFile as JC}from"node:child_process";import{promisify as YC}from"node:util";function Wf(t){return`refs/bostrat/wip/${t}`}function QC(t,e=Date.now()){return`refs/bostrat/rescue/${t}-${e}`}function D0(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function tM(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:ZC}async function qf(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Hn(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Hn(r,["remote","get-url","origin"])}catch{return P0.has(r)||(P0.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 yo(r,"bostrat: wip snapshot"),{stdout:s}=await Hn(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if($c.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=Wf(n);return await Hn(r,["push","--force","origin",`${o}:${i}`],B0),$c.set(r,a),await $({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
150
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Kf(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Hn(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Hn(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await yo(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await Hn(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if($c.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=QC(n);return await Hn(r,["push","origin",`${o}:${i}`],B0),$c.set(r,a),await $({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,rescue:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: rescued stranded work ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: rescue push failed: ${String(o?.message||o).split(`
|
|
151
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function j0({listTracks:t,sweepMs:e=tM(),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"||i?.transferredTo||await qf(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}}var XC,ZC,B0,Hn,$c,P0,Gf=M(()=>{Id();ut();XC=YC(JC),ZC=12e4,B0=6e4;Hn=(t,e,n=15e3)=>XC("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),$c=new Map,P0=new Set});import eM from"node:fs";import Vf from"node:fs/promises";import nM from"node:path";function aM(t=process.env){return!!(bt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function L0(t,{env:e=process.env,fetchImpl:n=(...o)=>fetch(...o),log:r=console}={}){let o=t?.id;if(!o)return{restored:!1,reason:"no track id"};if(!aM(e))return{restored:!1,reason:"plane not configured"};let s=ro(t),a=nM.join(s,`${o}.ndjson`);try{if(eM.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=bt(e),c={authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let d=new AbortController,p=setTimeout(()=>d.abort(),sM),m;try{m=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${rM}`,{headers:c,signal:d.signal})}finally{clearTimeout(p)}if(m.status===404)return{restored:!1,reason:"unknown to plane"};if(!m.ok)return{restored:!1,reason:`plane ${m.status}`};let w=await m.json();for(let y of w.events||[])l.push(JSON.stringify(y));if(l.length>oM)return{restored:!1,reason:"event count over safety cap"};if(w.next==null)break;u=w.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await Vf.mkdir(s,{recursive:!0});let f=`${a}.pull-${process.pid}-${Date.now()}`;return await Vf.writeFile(f,l.join(`
|
|
152
|
+
`)+`
|
|
153
|
+
`),await Vf.rename(f,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var rM,oM,sM,F0=M(()=>{zt();ut();rM=1e3,oM=5e5,sM=3e4});var xx={};Rl(xx,{HANDOFF_LATEST_REL:()=>Pr,KNOWN_AGENTS:()=>ba,addTrackRepo:()=>gp,adoptTrack:()=>Op,agentAvailability:()=>Nc,answerFullscreenOffer:()=>Ap,answerPrompt:()=>Bc,archiveTrack:()=>Br,archiveTrackForMergedPr:()=>Ye,augmentFirstPromptAfterHandoff:()=>Y0,buildAgentCmd:()=>z0,canResumeSubtask:()=>yx,cancelQueued:()=>Sa,cancelQueuedToDraft:()=>dp,cancelStopJob:()=>va,cancelSubtask:()=>Gc,changedRepos:()=>Eo,commitTransferOut:()=>qc,createSetupTrack:()=>kp,createTrack:()=>jc,deleteTrack:()=>Sp,demoteTransferredTracks:()=>$p,ensureLeadEpochs:()=>Hc,forceMergeSubtask:()=>Pp,getHandoffJob:()=>Ko,getSubtaskJob:()=>aN,getTrack:()=>Q,getTransferOutJob:()=>Ip,interruptLead:()=>Dc,isArchiving:()=>XM,isHandoffNoteEcho:()=>ep,isInterruptEcho:()=>np,isModelSwitchEcho:()=>rp,listSubtaskJobs:()=>Dr,listTracks:()=>at,matchTranscriptsToBoxes:()=>Q0,mergeNotifyMessage:()=>mx,navigatePrompt:()=>lp,notifyTrackForMergedPr:()=>Xe,notifyTrackForResolvedPr:()=>Lc,queueShipCompleteNotify:()=>fp,queuedMessages:()=>Uo,reapArchivedOrphans:()=>Tp,reconcileHandoffOutcome:()=>hx,reconcileInterruptedHandoffs:()=>_p,reconcileInterruptedSubtasks:()=>Cp,reconcileSubtaskOutcome:()=>gx,refreshStandingInstructions:()=>bp,rehydrateLead:()=>Fc,removeTrackRepo:()=>yp,reposOf:()=>se,resolveTrackBox:()=>Vs,resolveTrackBoxByHookToken:()=>ip,restartLead:()=>Uc,resumeAgentCmd:()=>ap,sayToLead:()=>pe,sendOptionsForAgent:()=>Re,setTrackDraft:()=>hp,setTrackModel:()=>wp,startLeadHandoff:()=>Wc,startSpinUpView:()=>pp,startSubtask:()=>Np,startTransferOut:()=>Rp,stopJobActive:()=>ox,subtaskMessages:()=>cp,trackMessages:()=>Pc,unarchiveTrack:()=>vp});import H from"node:fs/promises";import W from"node:path";import{randomUUID as ya}from"node:crypto";import{execFile as iM}from"node:child_process";import{promisify as cM}from"node:util";function z0(t,{skipPermissions:e=!0}={}){let n=Mc[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 ap(t,e,{skipPermissions:n=!0}={}){let r=Mc[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 ka(t){let e=Mc[t];if(e)return process.env[e.envVar]||e.base}async function Nc({maxAgeMs:t=6e4}={}){if(Wn.value&&Date.now()-Wn.at<t)return Wn.value;let e=Wn.value,n=await Promise.all(ba.map(async r=>{let o=await gi(ka(r),{retries:[]});return o===Xr?[r,e?.[r]??!0]:[r,o===wi]}));return Wn.value=Object.fromEntries(n),Wn.at=Date.now(),Wn.value}async function xa(t,e=!0){let n=z0(t,{skipPermissions:e});if(await gi(n)===Yr){Wn.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.":eu[r]?`Install it on this node: ${eu[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). ${o}`)}return n}function Y0(t,e=Pr){return`${t.trim()}
|
|
150
154
|
|
|
151
|
-
(Read ${e} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}function
|
|
152
|
-
`;return Cf=Cf.then(()=>U.mkdir(ln,{recursive:!0})).then(()=>U.appendFile($C,n)).catch(()=>{}),Cf}function v0(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=>DC.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 T0(t,e,n,{kind:r}={}){let o=await tt(e);if(!o)throw new Error(`box "${e}" is not running`);let s=da(o.agent);await Tn({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await Mt(e,n,{...Ie(o.agent),kind:r||"orchestration"}),await G({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await q(),i=Q(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);pt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await Y()}async function jC(t){let e=H.join(bi(t),`${t.id}.ndjson`),n;try{n=await U.stat(e)}catch{return null}let r=w0.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await L(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return w0.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function LC(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Rd(n.name,n.agent)}async function un(t,e=null){let n=e??new Map((await he()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=Ir.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([jC(t),LC(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:aa.has(t.id)}}function A0(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-FC,f=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function E0(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(f=>f.box.sessionId),a=_h(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=A0(u,a,f=>i.has(f));for(let f of d){let p=u.find(m=>m.name===f.box);p&&(p.sessionId=f.sessionId),await yh(f.box,f.sessionId),n=!0}}return n}async function et(){let t=await q();await E0(t)&&await Y();let e=new Map((await he()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>un(n,e)))}async function Ls(t,e){let n=await q();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function Wf(t){let e=Yo(t);if(!e)return null;let n=await q();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Sm(o.hookTokenHash,e))return{track:r,box:o};return null}async function z(t){let e=await q(),n=Q(e,t);return n?(await E0(e)&&await Y(),un(n)):null}function Lf(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(UC[t.agent]||[]).some(n=>n.test(e))}function Ff(t){let e=(t.text||"").trim();return e?HC.some(n=>n.test(e)):!1}async function vc(t,e={}){let n=await z(t);if(!n)return null;let r=new Set([...n.events.filter(b=>b.type==="orchestration-prompt"&&b.prompt).map(b=>String(b.prompt).trim()),...n.events.filter(b=>b.type==="said"&&b.text).map(b=>String(b.text).trim())]),o=No(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=Yd(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...qb(n),...l],f={windows:[...Qw(n),...tg(n,Ai),...a,...u]},m=(await Promise.all(n.boxes.map(b=>Yu(b,e)))).flat(),w=m.map(b=>{let v={kind:"message",...b};if(v.role==="assistant"&&(v.text||"").trim()&&v.tools?.length){let P=Is(v.agent).cliTools,mt=v.tools.filter(rt=>!P.has(rt));return{...v,tools:mt.length?mt:void 0}}return v}).map(b=>{if(b.role!=="user")return b;let v=Si(b.text);return v?v.kind==="suppress"?null:{...b,note:v,text:vw(v)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!Lf(b)).filter(b=>!Ff(b)).filter(b=>!jf(b)).filter(b=>!Rf(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!eg(b,f)),y=w.filter(b=>b.failed&&b.text===ku);if(y.length){let b=new Map;for(let v of y){if(!b.has(v.box)){let rt=[...(await yt(v.box,{lines:Ct}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Ot=>Ot[1]);b.set(v.box,rt.length?rt[rt.length-1]:null)}let P=b.get(v.box);P&&(v.text=`\u25A0 ${P}`)}}let k=await Promise.all(n.boxes.map(b=>Pg(b,e)));await Eb(n,k.flat()),await Tb(n,w),await Oy(n),await Xb(n,w).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await Ib(n,b),b.agent==="cursor"&&b.state==="live"&&await Mb(n,b);let S=await Promise.all(n.boxes.map(b=>Mg(b,e)));await Ab(n,S.flat().filter(b=>!Mf(b.box,b.ts,u)));let $=await L(n),R=c&&i.viewId?i.viewId:null,J=Uy($),C=lr($).filter(b=>!(b.kind==="message"&&(Lf(b)||Ff(b)||jf(b)||Rf(b)))&&!(b.kind==="view-card"&&R&&b.viewId===R)),D=Hy(C,J),A=C.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!D.has(b)&&Mf(b.box,b.ts,J))),N=Mu($,A).map(b=>({kind:"command-batch",id:b.id,ts:b.pending?b.ts:(b.anchorTs??b.ts)-1,anchorTs:b.anchorTs,anchorBox:b.anchorBox,agent:b.agent,boxRole:b.boxRole,commands:b.commands,pending:b.pending})).filter(b=>!Mf(b.anchorBox,b.anchorTs??b.ts,u)),W=$w(Ow([...A,...N].filter(b=>b.ts!=null).sort((b,v)=>b.ts-v.ts))),at=qC(n),it=at.size?W.filter(b=>b.kind==="event"||!at.has(b.box??b.anchorBox)):W,E=n.boxes.find(b=>b.name===n.leadBox)||null,O=E?await ps(E.cwd,E.sessionId,{fallbackNewest:!1}):null;if(!O&&E){let b=[...$].reverse().find(v=>v.type==="context.usage"&&v.boxId===E.name);b?.payload?.window&&(O={pct:b.payload.pct??(b.payload.occupancy!=null?b.payload.occupancy/b.payload.window:null),occupancy:b.payload.occupancy??null,window:b.payload.window,windowKnown:!0,model:b.payload.model||null})}let At=[...m].reverse().find(b=>b.role==="assistant"&&b.model),Gt=At?.model||null,gt=[...n.events||[]].reverse().find(b=>b.type==="model-change"),g=O?.model||Gt;gt&&(!At?.ts||gt.at>=At.ts)&&(g=gt.to);let h=E?await yt(E.name,{lines:Ct}).catch(()=>""):"",T=E&&ao(E.agent,h)?await Sn(E.name).catch(()=>h):h,_=E?wr(E,T):null;if(_&&E){let b=$i(E,_,T);if(b){let v=Yg(E.name,_.promptId);v.action!=="give-up"&&(v.action==="send"&&(Tt(E.name,b.keys,{kind:"auto-reject"}).catch(()=>{}),v.first&&I(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:E.name,provider:E.agent,payload:{ts:Date.now(),promptId:_.promptId,rule:b.id}}).catch(()=>{})),_=null)}}!_&&E&&!ws(E.agent,h)&&!Co(n.id).length&&(_=Ou(it,{leadBoxId:E.name,provider:E.agent,isAnswered:b=>$.some(v=>v.type==="prompt.answered"&&v.payload?.promptId===b)})),_&&Cy(n,E,_);let St=it.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),dt=!!E&&Ji(E.name,E.agent,h)&&!_;E&&!E.readyAt&&(dt||_||h&&pi(E.agent,h))&&(E.readyAt=Date.now(),await Y());let Jo=!!E&&!E.readyAt&&Date.now()-(n.createdAt||0)<WC;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&E&&pi(E.agent,h)&&!_){let b=await q(),v=Q(b,n.id);v?.setup&&!v.setup.connectedAt&&(v.setup.connectedAt=Date.now(),n.setup.connectedAt=v.setup.connectedAt,await I(v,{type:"provider.connected",actor:"system",source:"pane",boxId:E.name,provider:E.agent,payload:{provider:E.agent,ts:v.setup.connectedAt}}).catch(()=>{}),await Y())}let Bt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,K=null;if(c){let b=Date.now()-i.startedAt;if(i.viewId&&!dt){let v=rc(n.id);v&&(pt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:v.finishedAt}),await Y())}else if(_||b>Hb&&!dt&&!i.viewId||b>Jd){let v=_?"needs-input":b>Jd?"timeout":"no-view";rc(n.id,{error:v})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:E?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:v}}).catch(()=>{})}else K={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:St,context:O?{pct:O.pct,occupancy:O.occupancy,window:O.window,windowKnown:O.windowKnown,model:O.model}:null,models:{current:g,options:Ty(E?.agent)},pendingPrompt:_,working:dt,stopping:$0(n.id),leadStarting:Jo,handoff:Bt,view:K,queued:Co(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function qC(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 qf(t,e){let n=await z(t);if(!n)return null;let r=$r(t).find(d=>d.id===e)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await L(n),a=lr(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Mu(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await yt(o,{lines:Ct}).catch(()=>"");u=Ji(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Tc(t,{promptId:e,answers:n,text:r}={}){let o=Q(await q(),t);if(!o)throw new Error(`track "${t}" not found`);ma(t,"prompt-answer");let s=o.boxes.find(f=>f.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await tt(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await rd(s);if(i||(i=await KC(o,s)),!i)throw new Error("no pending prompt to answer");if(e&&e!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let f=typeof r=="string"?r.trim():"";if(!f)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(f)||f.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Mt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await Tt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let f=_w(i,n||[],typeof r=="string"?r:"");if(!f)throw new Error("no answer selected \u2014 pick an option or reply in chat");await fe(o.id,f,{kind:"prompt-answer"}),c={mode:"say",value:f,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let f=Gg(i,n||[]);await Tt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=Fh(i,n||[]);await Mt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:f,count:1}}if(i.answerMode==="say"){let f=i.questions||[];for(let p=0;p<f.length;p++){let m=f[p],w=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));Sd(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:p,questionTotal:i.questionTotal??f.length,header:m.header||"",question:m.question||"",multiSelect:!!m.multiSelect,chosen:w})}return await G({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(f=>l.options?.[f]).filter(Boolean).map(f=>({label:f.label,description:f.description||""})),d=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:u,...d?{control:d}:{}},await G({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Sd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function KC(t,e){if(!e)return null;let n=await L(t).catch(()=>[]);return Ou(lr(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Kf(t,{promptId:e,questionIndex:n}={}){let r=Q(await q(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await tt(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await rd(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=Vg(a,n);return c.length?(await Tt(r.leadBox,c,{kind:"navigate-prompt"}),await G({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Gf(t){let e=ra.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},ra.set(t,e)),e}function Co(t){return(ra.get(t)?.items??[]).map(e=>({...e}))}function pa(t){let e=ra.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}async function Vf(t){let e=pa(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=Q(await q(),t);if(o)return r=Mo(o,n),await Y(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function _0(t){let e=Gf(t);e.timer||(e.timer=setInterval(()=>VC(t).catch(()=>{}),GC))}async function VC(t){let e=ra.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Q(await q(),t);if(!n||n.archivedAt){pa(t);return}let r=No(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){_r(e,!1);return}let o=await tt(n.leadBox);if(!o){_r(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(fo(s?.agent)){_r(e,!1);return}let a=await yt(n.leadBox,{lines:Ct}).catch(()=>"");if(fi(s?.agent,a)){_r(e,!1);return}if(ao(s?.agent,a)){let c=await Sn(n.leadBox).catch(()=>a);if(wr(s,c)){_r(e,!1);return}}if(!_r(e,!0))return;let i=e.items.shift();e.idleSince=null,await R0(n,s,o,i.text,i.kind??null,i.attachments??null),await Y()}finally{e.flushing=!1}}}async function I0(t,e,n,r){let o=fo(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Mo(t,[n]),await Y()),my(t.id,o))try{await I(t,{type:"usage.cap.hit",actor:"system",provider:e?.agent??null,boxId:t.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${e?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function R0(t,e,n,r,o=null,s=null){await I0(t,e,r,o);let a=oa.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=S0(r,t.pendingHandoffHydrate.artifact||fa),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(l.length){let d=m0(l.map(f=>f.rel));i=i.trim()?`${i.trim()}
|
|
155
|
+
(Read ${e} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}function ep(t){return t.role==="user"&&dM.test((t.text||"").trim())}async function K(){return fe||(zf||=(async()=>{try{fe=JSON.parse(await H.readFile(V0,"utf8"))}catch{fe={tracks:[]}}finally{zf=null}return fe})(),zf)}async function J(){await Jt(V0,fe)}function mM(t,e){let n=JSON.stringify({trackId:t,...e})+`
|
|
156
|
+
`;return Jf=Jf.then(()=>H.mkdir(dn,{recursive:!0})).then(()=>H.appendFile(lM,n)).catch(()=>{}),Jf}function X0(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=>hM.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 Z0(t,e,n,{kind:r}={}){let o=await ot(e);if(!o)throw new Error(`box "${e}" is not running`);let s=ka(o.agent);await En({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await Nt(e,n,{...Re(o.agent),kind:r||"orchestration"}),await G({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await K(),i=Z(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);pt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await J()}async function wM(t){let e=W.join(ro(t),`${t.id}.ndjson`),n;try{n=await H.stat(e)}catch{return null}let r=U0.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await U(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return U0.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function gM(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Ud(n.name,n.agent)}async function Le(t,e=null){let n=e??new Map((await we()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=qn.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([wM(t),gM(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:wa.has(t.id)}}function Q0(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-yM,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 tx(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=Yh(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=Q0(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 Uh(d.box,d.sessionId),n=!0}}return n}async function at(){let t=await K();await tx(t)&&await J();let e=new Map((await we()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>Le(n,e)))}async function Vs(t,e){let n=await K();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function ip(t){let e=ss(t);if(!e)return null;let n=await K();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Gm(o.hookTokenHash,e))return{track:r,box:o};return null}async function Q(t){let e=await K(),n=Z(e,t);return n?(await tx(e)&&await J(),Le(n)):null}function np(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(bM[t.agent]||[]).some(n=>n.test(e))}function rp(t){let e=(t.text||"").trim();return e?kM.some(n=>n.test(e)):!1}async function Pc(t,e={}){let n=await Q(t);if(!n)return null;let r=new Set([...n.events.filter(b=>b.type==="orchestration-prompt"&&b.prompt).map(b=>String(b.prompt).trim()),...n.events.filter(b=>b.type==="said"&&b.text).map(b=>String(b.text).trim())]),o=Ko(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=lf(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...lk(n),...l],d={windows:[...Sg(n),...vg(n,Di),...a,...u]},m=(await Promise.all(n.boxes.map(b=>id(b,e)))).flat(),w=m.map(b=>{let I={kind:"message",...b};if(I.role==="assistant"&&(I.text||"").trim()&&I.tools?.length){let R=Ds(I.agent).cliTools,B=I.tools.filter(wt=>!R.has(wt));return{...I,tools:B.length?B:void 0}}return I}).map(b=>{if(b.role!=="user")return b;let I=Ni(b.text);return I?I.kind==="suppress"?null:{...b,note:I,text:Gw(I)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!np(b)).filter(b=>!rp(b)).filter(b=>!ep(b)).filter(b=>!Uf(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!Tg(b,d)),y=w.filter(b=>b.failed&&b.text===Ru);if(y.length){let b=new Map;for(let I of y){if(!b.has(I.box)){let wt=[...(await xt(I.box,{lines:Mt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(ht=>ht[1]);b.set(I.box,wt.length?wt[wt.length-1]:null)}let R=b.get(I.box);R&&(I.text=`\u25A0 ${R}`)}}let k=await Promise.all(n.boxes.map(b=>ey(b,e)));await Kb(n,k.flat()),await Wb(n,w),await Yy(n),await wk(n,w).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await Vb(n,b),b.agent==="cursor"&&b.state==="live"&&await Zb(n,b);let x=await Promise.all(n.boxes.map(b=>ty(b,e)));await qb(n,x.flat().filter(b=>!Yf(b.box,b.ts,u)));let _=await U(n),O=c&&i.viewId?i.viewId:null,z=ab(_),C=mr(_).filter(b=>!(b.kind==="message"&&(np(b)||rp(b)||ep(b)||Uf(b)))&&!(b.kind==="view-card"&&O&&b.viewId===O)),P=ib(C,z),T=C.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!P.has(b)&&Yf(b.box,b.ts,z))),D=Hu(_,T).map(b=>({kind:"command-batch",id:b.id,ts:b.pending?b.ts:(b.anchorTs??b.ts)-1,anchorTs:b.anchorTs,anchorBox:b.anchorBox,agent:b.agent,boxRole:b.boxRole,commands:b.commands,pending:b.pending})).filter(b=>!Yf(b.anchorBox,b.anchorTs??b.ts,u)),j=Qw(tg([...T,...D].filter(b=>b.ts!=null).sort((b,I)=>b.ts-I.ts))),it=SM(n),st=it.size?j.filter(b=>b.kind==="event"||!it.has(b.box??b.anchorBox)):j,E=n.boxes.find(b=>b.name===n.leadBox)||null,N=E?await xs(E.cwd,E.sessionId,{fallbackNewest:!1}):null;if(!N&&E){let b=[..._].reverse().find(I=>I.type==="context.usage"&&I.boxId===E.name);b?.payload?.window&&(N={pct:b.payload.pct??(b.payload.occupancy!=null?b.payload.occupancy/b.payload.window:null),occupancy:b.payload.occupancy??null,window:b.payload.window,windowKnown:!0,model:b.payload.model||null})}let Y=[...m].reverse().find(b=>b.role==="assistant"&&b.model),$t=Y?.model||null,dt=[...n.events||[]].reverse().find(b=>b.type==="model-change"),g=N?.model||$t;dt&&(!Y?.ts||dt.at>=Y.ts)&&(g=dt.to);let h=E?await xt(E.name,{lines:Mt}).catch(()=>""):"",v=E&&wo(E.agent,h)?await Tn(E.name).catch(()=>h):h,A=E?xr(E,v):null;if(A&&E){let b=Hi(E,A,v);if(b){let I=gy(E.name,A.promptId);I.action!=="give-up"&&(I.action==="send"&&(At(E.name,b.keys,{kind:"auto-reject"}).catch(()=>{}),I.first&&$(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:E.name,provider:E.agent,payload:{ts:Date.now(),promptId:A.promptId,rule:b.id}}).catch(()=>{})),A=null)}}!A&&E&&!Ts(E.agent,h)&&!Uo(n.id).length&&(A=Fu(st,{leadBoxId:E.name,provider:E.agent,isAnswered:b=>_.some(I=>I.type==="prompt.answered"&&I.payload?.promptId===b)})),A&&Xy(n,E,A);let tt=st.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),et=!!E&&ic(E.name,E.agent,h)&&!A;E&&!E.readyAt&&(et||A||h&&_i(E.agent,h))&&(E.readyAt=Date.now(),await J());let rr=!!E&&!E.readyAt&&Date.now()-(n.createdAt||0)<xM;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&E&&_i(E.agent,h)&&!A){let b=await K(),I=Z(b,n.id);I?.setup&&!I.setup.connectedAt&&(I.setup.connectedAt=Date.now(),n.setup.connectedAt=I.setup.connectedAt,await $(I,{type:"provider.connected",actor:"system",source:"pane",boxId:E.name,provider:E.agent,payload:{provider:E.agent,ts:I.setup.connectedAt}}).catch(()=>{}),await J())}let Ot=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,or=null;if(c){let b=Date.now()-i.startedAt;if(i.viewId&&!et){let I=hc(n.id);I&&(pt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:I.finishedAt}),await J())}else if(A||b>ik&&!et&&!i.viewId||b>cf){let I=A?"needs-input":b>cf?"timeout":"no-view";hc(n.id,{error:I})&&await $(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:E?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:I}}).catch(()=>{})}else or={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:tt,context:N?{pct:N.pct,occupancy:N.occupancy,window:N.window,windowKnown:N.windowKnown,model:N.model}:null,models:{current:g,options:Wy(E?.agent)},pendingPrompt:A,working:et,stopping:ox(n.id),leadStarting:rr,handoff:Ot,view:or,queued:Uo(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function SM(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 cp(t,e){let n=await Q(t);if(!n)return null;let r=Dr(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 U(n),a=mr(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Hu(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await xt(o,{lines:Mt}).catch(()=>"");u=ic(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Bc(t,{promptId:e,answers:n,text:r}={}){let o=Z(await K(),t);if(!o)throw new Error(`track "${t}" not found`);va(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 ot(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await md(s);if(i||(i=await vM(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 Nt(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await At(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=Yw(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await pe(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=py(i,n||[]);await At(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=lw(i,n||[]);await Nt(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:d,count:1}}if(i.answerMode==="say"){let d=i.questions||[];for(let p=0;p<d.length;p++){let m=d[p],w=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));Nd(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:p,questionTotal:i.questionTotal??d.length,header:m.header||"",question:m.question||"",multiSelect:!!m.multiSelect,chosen:w})}return await G({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(d=>l.options?.[d]).filter(Boolean).map(d=>({label:d.label,description:d.description||""})),f=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:u,...f?{control:f}:{}},await G({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Nd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function vM(t,e){if(!e)return null;let n=await U(t).catch(()=>[]);return Fu(mr(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function lp(t,{promptId:e,questionIndex:n}={}){let r=Z(await K(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await ot(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await md(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=my(a,n);return c.length?(await At(r.leadBox,c,{kind:"navigate-prompt"}),await G({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function up(t){let e=fa.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},fa.set(t,e)),e}function Uo(t){return(fa.get(t)?.items??[]).map(e=>({...e}))}function Sa(t){let e=fa.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}async function dp(t){let e=Sa(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=Z(await K(),t);if(o)return r=Ho(o,n),await J(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function ex(t){let e=up(t);e.timer||(e.timer=setInterval(()=>AM(t).catch(()=>{}),TM))}async function AM(t){let e=fa.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Z(await K(),t);if(!n||n.archivedAt){Sa(t);return}let r=Ko(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Nr(e,!1);return}let o=await ot(n.leadBox);if(!o){Nr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(vr(s?.agent)){Nr(e,!1);return}let a=await xt(n.leadBox,{lines:Mt}).catch(()=>"");if(Ei(s?.agent,a)){Nr(e,!1);return}if(wo(s?.agent,a)){let c=await Tn(n.leadBox).catch(()=>a);if(xr(s,c)){Nr(e,!1);return}}if(!Nr(e,!0))return;let i=e.items.shift();e.idleSince=null,await rx(n,s,o,i.text,i.kind??null,i.attachments??null),await J()}finally{e.flushing=!1}}}async function nx(t,e,n,r){let o=vr(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Ho(t,[n]),await J()),My(t.id,o))try{await $(t,{type:"usage.cap.hit",actor:"system",provider:e?.agent??null,boxId:t.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${e?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function rx(t,e,n,r,o=null,s=null){await nx(t,e,r,o);let a=pa.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=Y0(r,t.pendingHandoffHydrate.artifact||Pr),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=M0(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
|
|
153
157
|
|
|
154
|
-
${
|
|
158
|
+
${f}`:f}let u=pt(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...l.length?{attachments:l}:{}});Fo(t,null);try{if(await En({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:ka(n.agent)}),(pa.get(t.id)||0)!==a){!o&&r.trim()&&(Ho(t,[r]),await J());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}await Nt(t.leadBox,i,{...Re(e?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){throw pt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,...f?.interrupted?{reason:"interrupted"}:{}}),f}await FM(t,e,i)||(await sx(t.leadBox,e?.agent,i.length+8).catch(()=>{}),pt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,reason:"unsubmitted"}),o||Ho(t,[r])),c&&await G({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function fp(t,{actions:e}={}){up(t).items.push({id:ya(),text:cb(e),ts:Date.now(),kind:"ship-notify"}),ex(t)}async function pe(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"||!go(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Z(await K(),t);if(!s)throw new Error(`track "${t}" not found`);if(s.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before driving it`);let a=s.boxes.find(d=>d.name===s.leadBox),i=go(e);await nx(s,a,i,n),va(t,"say");let c=await ot(s.leadBox);if(!c&&await Fc(s)&&(await J(),c=await ot(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=up(t),u=await xt(s.leadBox,{lines:Mt}).catch(()=>""),f=l.items.length>0||Ei(a?.agent,u);if(!f&&wo(a?.agent,u)){let d=await Tn(s.leadBox).catch(()=>u);f=!!xr(a,d)}if(f){let d={id:ya(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),ex(t),Fo(s,null),await J(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await rx(s,a,c,i,n,o),await J(),{ok:!0,leadBox:s.leadBox}}async function pp(t,{hint:e}={}){let n=Z(await K(),t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it first`);let r=lf(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";ck(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${H0}
|
|
155
159
|
|
|
156
|
-
What to preview: ${String(e).trim()}`:
|
|
160
|
+
What to preview: ${String(e).trim()}`:H0;try{await pe(t,a,{kind:"spin-up-view"})}catch(i){throw hc(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Dc(t){let e=Z(await K(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);pa.set(t,(pa.get(t)||0)+1);let n=Sa(t),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Lo.get(t);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Ho(e,r),await J()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:e.draftRev||0}}let s=OM(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,Kn(EM)]);let a=Z(await K(),t)||e;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}function ox(t){let e=Lo.get(t);return!!e&&!e.canceled}function va(t,e){let n=Lo.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function mp(t){va(t,"track-ended"),pa.delete(t),Sa(t)}function OM(t,{droppedCount:e,droppedTexts:n}){let r={startedAt:Date.now(),clicks:1,escSends:0,bgSends:0,queuedTexts:[...n],canceled:!1,cancelReason:null,result:null,restored:null},o;return r.settled=new Promise(s=>{o=s}),Lo.set(t,r),(async()=>{let s=Z(await K(),t),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await ot(s.leadBox):null,c=!1,l=!1;if(i){t:for(let y=0;y<_M&&!r.canceled;y++){await At(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of IM){await Kn(k);let x=await xt(s.leadBox,{lines:Mt}).catch(()=>"");if(!Ts(a?.agent,x)){c=!0;break t}}}if(!c){l=!0;for(let y=0;y<$M&&!r.canceled&&(await Kn(RM),!r.canceled);y++){let k=await xt(s.leadBox,{lines:Mt}).catch(()=>"");if(!Ts(a?.agent,k)){c=!0;break}await At(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=Z(await K(),t)||s,f=u?.boxes.find(y=>y.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await PM(u,f).catch(()=>null),d||(p=await jM(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await MM(u).catch(()=>null):null,w=[d||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=Ho(u,w),await J()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await CM(u||s,f,{ts:r.startedAt,result:r.result,dropped:e,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await G({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:t,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),Lo.get(t)===r&&Lo.delete(t),o()}),r}async function CM(t,e,n){await $(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}async function MM(t){let e=vc(await U(t));if(!e?.offerId)return null;let n=e.offerType==="exists";return await $(t,{type:n?"pr.exists.answered":"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,...n?{choice:"dismiss"}:{action:"dismiss",actions:null},reason:"interrupt"}}),e.offerId}async function PM(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 NM){await Kn(r);let o=await xt(t.leadBox,{lines:Mt}).catch(()=>""),s=Cs(e.agent,o);if(s&&$u(n.text,s))return await sx(t.leadBox,e.agent,n.text.length+8),pt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),n.text}return null}async function jM(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>BM||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!It(r.text))return null;for(let s of DM){await Kn(s);let a;try{a=await id(e,{})}catch{return null}if(jw(a,r))return null;let i=await xt(t.leadBox,{lines:Mt}).catch(()=>"");if(Cs(e.agent,i))return null}return pt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function FM(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=Xf;r++){let o=!1;for(let s of LM){await Kn(s);let a=await xt(t.leadBox,{lines:Mt}).catch(()=>""),i=Lw(e.agent,n,a,{busy:Ts(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Xf&&(await At(t.leadBox,["Enter"],{kind:"submit-retry"}),G({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return G({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:Xf,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function sx(t,e,n){for(let r=0;r<3;r++){await to(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await Kn(200);let o=await xt(t,{lines:Mt}).catch(()=>""),s=Cs(e,o);if(!s)return!0;n=s.length+8}return!1}function Fo(t,e,{source:n}={}){t.draftRev=(t.draftRev||0)+1;let r=typeof e=="string"?e:"";return r.trim()?t.draft={text:r,updatedAt:Date.now(),...n?{source:n}:{}}:delete t.draft,t.draft||null}function Ho(t,e){let n=[],r=o=>n.some(s=>It(s).includes(It(o)));for(let o of[t.draft?.text,...e])o&&o.trim()&&!r(o)&&n.push(o);return n.length?Fo(t,n.join(`
|
|
157
161
|
|
|
158
|
-
`),{source:"restore"})?.text??null:null}function
|
|
162
|
+
`),{source:"restore"})?.text??null:null}function HM(t,e){let n=It(e);if(!n)return!1;if(Uo(t.id).some(o=>It(o.text)===n))return!0;let r=t.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>UM)break;if(!(s.type!=="said"||!s.text||It(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function hp(t,e,{rev:n=null}={}){if(typeof e!="string")throw new Error("text (string) required");let r=Z(await K(),t);if(!r)throw new Error(`track "${t}" not found`);let o=e,s=go(e);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&HM(r,s))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&!o.trim()&&r.draft?.text)return{ok:!0,draft:r.draft,rev:r.draftRev||0,ignored:"stale-clear"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let i=r.draft.text;It(o).includes(It(i))?Fo(r,o):Fo(r,`${i}
|
|
159
163
|
|
|
160
|
-
${o}`,{source:"restore"})}else
|
|
161
|
-
`);return`${
|
|
164
|
+
${o}`,{source:"restore"})}else Fo(r,o);return await J(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function wp(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Z(await K(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await ot(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=qy(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await xs(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await En({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ka(o.agent)});let u=await Ky(a,r.leadBox,i);return pt(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 ax(t){for(let e of t){if(!e||!await Wi(e))continue;let n=await xy(e).catch(()=>null);if(!n)continue;if((await H.stat(W.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function se(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:W.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function Eo(t){let e=[];for(let n of se(t?.workspace)){if(!n?.dir)continue;let r=await xd(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Sd(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function ix(t){let e=t;typeof e=="string"&&!W.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await Kr(e)??await ri(e));let n=await ax([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 WM(t,e,n,r){let o=`track/${Mp(e)}-${t}`,s=[];try{await H.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await ix(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await lt(c).catch(()=>null),u=W.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${W.basename(c)}-${p}`;a.add(u);let f=W.join(n,u),d=await ko(c);await bo(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await Ta(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await ne(i.base,i.dir).catch(()=>{});throw await H.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function gp(t,e){let n=await K(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await ix(typeof e=="string"?e.trim():e);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await lt(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=W.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${W.basename(s)}-${d}`;let l=W.join(o.dir,c),u=await ko(s);await Sy(s,o.branch)?await qi(s,l,o.branch):await bo(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 Ta(o.dir,o.repos,o.branch),pt(r,"retagged",{action:"add",repo:a??s,name:c,workspace:cx(o)}),await J(),f}async function yp(t,e){let n=await K(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===e||c.repo===e||c.base===e));if(!s)throw new Error(`repo "${e}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of oe.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 xd(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Sd(s.dir,s.baseRef).catch(()=>0);if(a||i>0)throw new Error(`"${s.name}" has work (${a?"uncommitted changes":`${i} commit${i===1?"":"s"} ahead of ${s.baseRef??"base"}`}) \u2014 ship or discard it first`);return await ne(s.base,s.dir).catch(()=>{}),await H.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Ta(o.dir,o.repos,o.branch),pt(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:cx(o)}),await J(),{removed:s.name}}async function qM(t,e,{repo:n,repos:r,cwd:o}={}){await H.mkdir(ua,{recursive:!0});let s=W.join(ua,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return WM(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!W.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Kr(n)??await ri(n));let i=await ax([n,o,uM]);if(i){let c=`track/${Mp(e)}-${t}`;try{let l=await ko(i);await bo(i,s,c,l),await ha(s);let u=await lt(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await H.mkdir(s,{recursive:!0}),await vd(s),await ha(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ha(t){try{await H.writeFile(W.join(t,"AGENTS.md"),la,{flag:"wx"})}catch{return}await zM(t,["/AGENTS.md"])}function VM(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
165
|
+
`);return`${GM}
|
|
162
166
|
|
|
163
167
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
164
168
|
git worktree per tagged repo, ALL on the same branch \`${e}\`:
|
|
@@ -170,18 +174,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
170
174
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
171
175
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
172
176
|
or preview an app, run it from that app's own repo directory.
|
|
173
|
-
`}async function
|
|
174
|
-
`).includes(s));o.length&&await
|
|
177
|
+
`}async function Ta(t,e,n){let r=VM(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await H.writeFile(W.join(t,o),r).catch(()=>{})}async function ux(t){let{stdout:e}=await G0("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return W.resolve(t,e.trim())}async function zM(t,e){try{let n=await ux(t);await H.mkdir(W.dirname(n),{recursive:!0});let r=await H.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
|
|
178
|
+
`).includes(s));o.length&&await H.appendFile(n,(r.endsWith(`
|
|
175
179
|
`)||!r?"":`
|
|
176
180
|
`)+o.join(`
|
|
177
181
|
`)+`
|
|
178
|
-
`)}catch{}}async function
|
|
182
|
+
`)}catch{}}async function JM(t,e){try{let n=await ux(t),r=await H.readFile(n,"utf8"),o=r.split(`
|
|
179
183
|
`).filter(s=>s!==e);o.length!==r.split(`
|
|
180
|
-
`).length&&await
|
|
181
|
-
`))}catch{}}async function
|
|
184
|
+
`).length&&await H.writeFile(n,o.join(`
|
|
185
|
+
`))}catch{}}async function bp(){let t={refreshed:0,removed:0,codexGlobal:null};try{await H.mkdir(dn,{recursive:!0}),await H.writeFile(W.join(dn,"CLAUDE.md"),KM).catch(()=>{}),await H.writeFile(W.join(dn,"AGENTS.md"),la).catch(()=>{});let e=process.env.CODEX_HOME||W.join(process.env.HOME||"/tmp",".codex");if((await H.stat(e).catch(()=>null))?.isDirectory()){let r=W.join(e,"AGENTS.md"),o=await H.readFile(r,"utf8").catch(()=>null);o===null?(await H.writeFile(r,Qf,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Zf(o)&&o!==Qf&&(await H.writeFile(r,Qf).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[ua,qo]){let o=await H.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=W.join(r,s.name),i=W.join(a,"AGENTS.md"),c=await H.readFile(i,"utf8").catch(()=>null);c!==null&&Zf(c)&&c!==la&&(await H.writeFile(i,la).catch(()=>{}),t.refreshed++);let l=W.join(a,"CLAUDE.md"),u=await H.readFile(l,"utf8").catch(()=>null);u!==null&&Zf(u)&&(await H.rm(l,{force:!0}).catch(()=>{}),await JM(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function dx(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of se(e))n.base&&n.dir&&await ne(n.base,n.dir).catch(()=>{});await H.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await ne(e.base,e.dir):await H.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function jc({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 xa(o,s),i=await K(),c=ya().slice(0,8),l=await qM(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(w=>w.name))),f=`${t.trim()} lead`;u.has(yi(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await vn(f,l.dir,{agentCmd:a,agent:o});As({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:[Ep(d,"lead","live")],events:[],createdAt:Date.now()};return pt(p,"opened",{agent:o,box:d.name,skipPermissions:s,name:p.name,cwd:p.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:m,base:w,baseRef:y,repo:k})=>({name:m,base:w,baseRef:y,repo:k}))}:{}}}),i.tracks.push(p),await J(),Le(p)}async function kp({provider:t="claude",fresh:e=!1}={}){if(!YM.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await K()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await Le(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await Br(r.id).catch(()=>{})}let o=(await xa(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=ya().slice(0,8),a=W.join(dn,"setup",s);await H.mkdir(a,{recursive:!0});let i=await vn(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&As({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:[Ep(i,"lead","live")],events:[],createdAt:Date.now()};return pt(c,"opened",{agent:t,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await K()).tracks.push(c),await J(),Le(c)}async function xp(t,e,n){await hk(t).catch(()=>{});let r=Wo.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Wo.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of Dr(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Bp(a).catch(()=>{}),oe.delete(a.id);for(let a of Object.values(e.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await K(),s=new Set((o.tracks||[]).filter(a=>a.id!==t&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of e.boxes)s.has(a.name)||await Ie(a.name).catch(()=>{})}async function Sp(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await K(),o=r.tracks.findIndex(a=>a.id===t);if(o===-1)throw new Error(`track "${t}" not found`);let s=r.tracks[o];return e||await Xk(s),mp(t),await xp(t,s,"track deleted"),n?await I0(t).catch(()=>{}):await _0(t).catch(()=>{}),await zy(),await dx(s),e&&await Zk(t).catch(()=>{}),e?await h0(t).catch(()=>{}):await m0(s).catch(()=>{}),pt(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 Br(t){let e=await K(),n=Z(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);return n.archivedAt=Date.now(),mp(t),pt(n,"archived",{boxes:n.boxes.length}),await J(),n.kind==="setup"?(await fx(t),Le(n)):(wa.add(t),QM(t).catch(()=>{}).finally(()=>wa.delete(t)),{...await Le(n),archiving:!0})}async function QM(t){try{await tN(t)}catch{}await fx(t).catch(()=>{})}async function fx(t){let e=await K(),n=Z(e,t);if(!(!n||!n.archivedAt)){await xp(t,n,"track archived");for(let r of n.boxes)qn.has(r.state)||(r.state="stopped");await J()}}async function tN(t){let e=await K(),n=Z(e,t);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await ot(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await kg(n.leadBox,i.cwd,{timeoutMs:ZM})),s="lead-cli"}catch{}if(r||(r=Ng(n,await U(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await $(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await J()}async function Ye(t,e){let n=await qt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await px(n);if(r.length){for(let o of r)await Er(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Br(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function px(t){return(await _r().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 Xe(t,e){return Lc(t,e,"merged")}async function Lc(t,e,n="merged"){let r=await qt(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 px(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await Eb(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await pe(r.trackId,mx(a),{kind:"merge-notify"}),!0}catch{return!1}}function mx(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 ${W0[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 ${W0[i.outcome]??i.outcome}`).join(`
|
|
182
186
|
`),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}
|
|
183
187
|
${s}
|
|
184
|
-
${a}`}async function Rc(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||Ir.has(e.state)||await tt(e.name))return!1;let n=Hf(e.agent,e.sessionId),r=await oi(n);if(r===Hr)return pt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===Wr&&console.warn(`[track] ${e.name}: could not confirm "${e.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await or(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=Yo(o.hookToken),mi({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",pt(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(o){return pt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(o?.message||o)}),!1}}async function rp(t){let e=await q(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,aa.delete(t),pt(n,"unarchived",{}),await Rc(n),await Y(),un(n)}async function op(){let t=await q(),e=new Set((await he()).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 _e(a.name).catch(()=>{}),pt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Ir.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Y(),r}async function $c(t){let e=await q(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(Ir.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await tt(n.leadBox)){let a=await yt(n.leadBox,{lines:Ct}).catch(()=>"");if(fi(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await _e(n.leadBox),r.state="stopped"}let s=await Rc(n);if(await Y(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return un(n)}async function sp(t,e,n){let r=await z(t);if(!r)throw new Error(`track "${t}" not found`);if(!e)throw new Error("unknown offer");let o=await L(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===e))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===e))throw new Error("offer already answered");return n&&await $c(t),await I(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:e,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function ap(t){return(!Array.isArray(t.epochs)||t.epochs.length===0)&&(t.epochs=[{agent:t.agent??null,sessionId:t.sessionId??null,pinned:t.pinned===!0,startedAt:t.createdAt??null,endedAt:null}]),t.epochs}async function bc(t,e){let n=await q(),r=Q(n,t);r&&(r.handoff={...r.handoff||{},...e},await Y())}function W0(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 ip(){let t=await q(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=W0(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await tt(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=ap(l),f=d[d.length-1],p=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:fa,at:u},pt(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await G({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await G({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await _e(i).catch(()=>{}),a&&await _e(a).catch(()=>{})}return e&&await Y(),e}async function Oc(t,e,n=!0,r=null){let o=ia.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Q(await q(),t);if(!s)throw new Error(`track "${t}" not found`);ma(t,"handoff");let a=await Sc(e,n),i=fo(e);if(i){let u=new Error(`can't hand off to ${e} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await tt(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:v0(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ia.set(t,l),await bc(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),TM(l,s,c,a,e).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${l.step}": ${l.error}`),await bc(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),G({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),G({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function TM(t,e,n,r,o){let s=async(R,J={})=>{t.step=R,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${R}`),await bc(t.trackId,{step:R,toBox:t.toBox}),await G({flow:"lead-handoff",phase:R,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:J.sessionName,...J})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await q(),i=Q(a,t.trackId)?.boxes.find(R=>R.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:t.fromBox}),t.checkpoint="llm",fo(c))t.checkpoint="skipped-capped";else try{await og(t.fromBox,{timeoutMs:CC})}catch(R){t.checkpoint="reconstructed",await G({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:t.trackId,fromBox:t.fromBox,error:R?.message||String(R)}).catch(()=>{})}t.checkpoint!=="llm"&&(t.statusArtifact=await sg(t.trackId,n.cwd,{trackName:e.name||e.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:t.fromBox,checkpoint:t.checkpoint,statusArtifact:t.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await Uu(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:da(o)});let p=await yt(t.fromBox,{lines:Ct}).catch(()=>"");if(!pi(o,p)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let R=Hf(c,l,{skipPermissions:t.skipPermissions});await Uu(t.fromBox,{agentCmd:R,agent:c,resumeSessionId:l,pinned:u});let J=await q(),C=Q(J,t.trackId);if(C){let D=C.boxes.find(A=>A.name===t.fromBox);D&&(D.state="live"),pt(C,"lead-handoff-failed",{box:t.fromBox,fromAgent:c,toAgent:o,reason:t.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:t.startedAt}),await Y()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let m=await q(),w=Q(m,t.trackId);if(!w)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=w.boxes.find(R=>R.name===t.fromBox);if(!y)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let k=Date.now(),S=ap(y),$=S[S.length-1];$.endedAt=k,t.reason&&($.endReason=t.reason),S.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:k,endedAt:null}),y.agent=o,y.sessionId=f.sessionId,y.pinned=f.pinned,y.state="live",y.role="lead",w.pendingHandoffHydrate={artifact:t.statusArtifact||fa,at:k},pt(w,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:S.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt,checkpoint:t.checkpoint}),await Y(),await s("done",{sessionName:t.fromBox}),await bc(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}async function q0(t,e,n){let r=await q(),o=Q(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await Y())}async function Ro(t,e){t.step=e,await ha(t)}function K0(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 G0(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function IM(t,e,n){if(!G0(n)||de.has(e)||!await tt(n.box).catch(()=>null))return!1;try{await U.access(n.dir)}catch{return!1}let o=await tt(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return de.set(e,s),z0(s,o).catch(a=>V0(s,a)),!0}async function cp(){let t=await q(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(de.has(r))continue;if(await IM(n,r,o)){e=!0,pt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await G({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=K0(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Ir.has(i.state)&&(i.state="canceled"),pt(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await G({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await b0(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Ir.has(r.state)||de.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?H.join(Po,`${n.id}-${r.subtaskId}`):null;await G({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await b0(n,r.name,o)}}return e=await RM(t)||e,e&&await Y(),e}async function b0(t,e,n){if(e&&await _e(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of pe(t.workspace)){let s=H.join(n,o.name);await ae(o.base,s).then(()=>!0,()=>!1)||await ae(o.dir,s).catch(()=>{})}await U.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await tt(t.leadBox).catch(()=>null);r?.cwd&&await ae(r.cwd,n).catch(()=>{})}async function RM(t){let e;try{e=await U.readdir(Po)}catch{return!1}let n=new Set;for(let o of t.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of de.values())o.dir&&n.add(H.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=H.join(Po,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of pe(a.workspace))await ae(i.base,H.join(s,i.name)).catch(()=>{});await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await tt(a.leadBox).catch(()=>null):null;i?.cwd?await ae(i.cwd,s).catch(()=>{}):await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await G({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function up(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!io(n))throw new Error("task (string) required");let s=await Sc(e,r),a=fo(e);if(a){let p=new Error(`can't delegate to ${e} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=Q(await q(),t);if(!i)throw new Error(`track "${t}" not found`);let c=await tt(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=pe(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of p)if(!m?.dir||!await Oi(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);u=p.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await Oi(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=la().slice(0,8),f={id:d,trackId:t,trackName:i.name,agent:e,task:io(n),skipPermissions:r,reason:v0(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${lp(i.name)}-${d}`,base:null,repos:u,dir:H.join(Po,`${t}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return de.set(d,f),await ha(f),$M(f,c,s).catch(p=>V0(f,p)),f}async function V0(t,e){t.failedStep=t.step,t.error=e instanceof ca?"canceled":e?.message||String(e),t.step=e instanceof ca?"canceled":"error",t.finishedAt=Date.now(),await fp(t).catch(()=>{}),await ha(t).catch(()=>{}),await PM(t).catch(()=>{})}function Mc(t){let e=de.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 dp(t){let e=de.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 $M(t,e,n){if(await Ro(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await Rs(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await ha(t)}else t.base=await Rs(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await Ro(t,"spawn"),await U.mkdir(Po,{recursive:!0}),t.repos){await U.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await co(a.leadDir,H.join(t.dir,a.name),t.branch,a.base);let s=pe((Q(await q(),t.trackId)||{}).workspace);await Ec(t.dir,t.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),t.branch)}else await co(e.cwd,t.dir,t.branch,t.base),await Uf(t.dir);await OM(t,e);let r=t.trackName.slice(0,38).trim(),o=await or(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await CM(t,o),await T0(t.trackId,o.name,PC(t.task,!!t.repos),{kind:"subtask"}),await z0(t,e)}async function z0(t,e){await Ro(t,"working");let n=H.join(t.dir,yc);if(await BM(n,t),t.canceled)throw new ca;if(await Ro(t,"merge"),t.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of t.repos){let u=H.join(t.dir,l.name);await ld(u,`bostrat: subtask ${t.id} result (auto-committed)`),l.stat=await pd(l.leadDir,l.base,t.branch,Io),l.files=await md(l.leadDir,l.base,t.branch,Io);let d=await hd(l.leadDir,l.base,t.branch,Io);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(f=>H.join(l.name,f))),i.push(...l.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${l.name}: ${f}`:H.join(l.name,f))),d.applied||(c=!1)}t.stat=s,t.files=a,t.conflicts=i,t.applied=c}else{await ld(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await pd(e.cwd,t.base,t.branch,Io),t.files=await md(e.cwd,t.base,t.branch,Io);let s=await hd(e.cwd,t.base,t.branch,Io);t.applied=s.applied,t.conflicts=s.conflicts}await ha(t);let r=await NM(t,e,n);await Ro(t,"resume"),await T0(t.trackId,t.leadBox,NC(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await Ro(t,"reap"),await fp(t),t.step="done";let o=Date.now();await q0(t.trackId,t.id,{...Cc(t),finishedAt:o}),await MM(t,o),t.finishedAt=o}async function OM(t,e){let n=H.join(t.dir,H.dirname(na));await U.mkdir(n,{recursive:!0});let r="";try{r=await Rh(e.cwd,e.sessionId)}catch{}if(!r)try{r=await U.readFile(H.join(e.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${t.id}
|
|
188
|
+
${a}`}async function Fc(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||qn.has(e.state)||await ot(e.name))return!1;let n=ap(e.agent,e.sessionId),r=await gi(n);if(r===Yr)return pt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===Xr&&console.warn(`[track] ${e.name}: could not confirm "${e.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await vn(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=ss(o.hookToken),As({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",pt(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(o){return pt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(o?.message||o)}),!1}}async function vp(t){let e=await K(),n=Z(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,wa.delete(t),pt(n,"unarchived",{}),await Fc(n),await J(),Le(n)}async function Tp(){let t=await K(),e=new Set((await we()).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 Ie(a.name).catch(()=>{}),pt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!qn.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await J(),r}async function Uc(t){let e=await K(),n=Z(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(qn.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ot(n.leadBox)){let a=await xt(n.leadBox,{lines:Mt}).catch(()=>"");if(Ei(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ie(n.leadBox),r.state="stopped"}let s=await Fc(n);if(await J(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Le(n)}async function Ap(t,e,n){let r=await Q(t);if(!r)throw new Error(`track "${t}" not found`);if(!e)throw new Error("unknown offer");let o=await U(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===e))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===e))throw new Error("offer already answered");return n&&await Uc(t),await $(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:e,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function Hc(t){return(!Array.isArray(t.epochs)||t.epochs.length===0)&&(t.epochs=[{agent:t.agent??null,sessionId:t.sessionId??null,pinned:t.pinned===!0,startedAt:t.createdAt??null,endedAt:null}]),t.epochs}async function Cc(t,e){let n=await K(),r=Z(n,t);r&&(r.handoff={...r.handoff||{},...e},await J())}function hx(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 _p(){let t=await K(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=hx(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ot(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=Hc(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Pr,at:u},pt(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await G({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await G({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ie(i).catch(()=>{}),a&&await Ie(a).catch(()=>{})}return e&&await J(),e}async function Wc(t,e,n=!0,r=null){let o=Wo.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Z(await K(),t);if(!s)throw new Error(`track "${t}" not found`);va(t,"handoff");let a=await xa(e,n),i=vr(e);if(i){let u=new Error(`can't hand off to ${e} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await ot(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:X0(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Wo.set(t,l),await Cc(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),eN(l,s,c,a,e).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${l.step}": ${l.error}`),await Cc(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),G({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),G({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function eN(t,e,n,r,o){let s=async(O,z={})=>{t.step=O,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${O}`),await Cc(t.trackId,{step:O,toBox:t.toBox}),await G({flow:"lead-handoff",phase:O,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:z.sessionName,...z})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await K(),i=Z(a,t.trackId)?.boxes.find(O=>O.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:t.fromBox}),t.checkpoint="llm",vr(c))t.checkpoint="skipped-capped";else try{await Ku(t.fromBox,{timeoutMs:J0})}catch(O){t.checkpoint="reconstructed",await G({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:t.trackId,fromBox:t.fromBox,error:O?.message||String(O)}).catch(()=>{})}t.checkpoint!=="llm"&&(t.statusArtifact=await Gu(t.trackId,n.cwd,{trackName:e.name||e.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:t.fromBox,checkpoint:t.checkpoint,statusArtifact:t.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await Zu(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await En({cwd:n.cwd,sessionId:d.sessionId,sessionName:t.fromBox,agent:o,agentCmd:ka(o)});let p=await xt(t.fromBox,{lines:Mt}).catch(()=>"");if(!_i(o,p)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let O=ap(c,l,{skipPermissions:t.skipPermissions});await Zu(t.fromBox,{agentCmd:O,agent:c,resumeSessionId:l,pinned:u});let z=await K(),C=Z(z,t.trackId);if(C){let P=C.boxes.find(T=>T.name===t.fromBox);P&&(P.state="live"),pt(C,"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 m=await K(),w=Z(m,t.trackId);if(!w)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=w.boxes.find(O=>O.name===t.fromBox);if(!y)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let k=Date.now(),x=Hc(y),_=x[x.length-1];_.endedAt=k,t.reason&&(_.endReason=t.reason),x.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:k,endedAt:null}),y.agent=o,y.sessionId=d.sessionId,y.pinned=d.pinned,y.state="live",y.role="lead",w.pendingHandoffHydrate={artifact:t.statusArtifact||Pr,at:k},pt(w,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:x.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt,checkpoint:t.checkpoint}),await J(),await s("done",{sessionName:t.fromBox}),await Cc(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}async function Rp(t){let e=sp.get(t);if(e&&!e.finishedAt)throw new Error("a transfer is already running for this Track");let n=await K(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 restore it before moving it");if(r.kind==="setup")throw new Error("setup boxes don't move");if(r.transferredTo)throw new Error(`track already transferred to ${r.transferredTo}`);let o=Wo.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of oe.values())if(a.trackId===t&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:t,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return sp.set(t,s),nN(s,r).catch(a=>{s.error=a?.message||String(a),s.finishedAt=Date.now(),console.error(`[transfer-out ${t}] FAILED at step "${s.step}": ${s.error}`)}),s}async function nN(t,e){let n=e.boxes.find(u=>u.name===e.leadBox)||null,r=n?await ot(n.name).catch(()=>null):null;if(t.checkpoint="llm",!r||n&&vr(n.agent))t.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Ku(e.leadBox,{timeoutMs:J0})}catch{t.checkpoint="reconstructed"}t.checkpoint!=="llm"&&await Gu(e.id,e.cwd,{trackName:e.name||e.id}).catch(()=>null),t.step="push";let o=e.workspace?.branch??null,s=[];if(o)for(let u of se(e.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await un(u.dir,["push","origin",o],op);let{stdout:d}=await un(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
189
|
+
`)[0]}s.push(f)}t.step="wip";let a=await qf({id:e.id,cwd:e.cwd}),i=await K(),c=Z(i,e.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&Hc(l),await J(),t.result={name:c?.name??e.name,agent:l?.agent??null,leadBox:c?.leadBox??e.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:f,baseSha:d})=>({name:u,repo:f,baseSha:d})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:f,error:d})=>({name:u,repo:f,error:d})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:t.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},t.step="ready",t.finishedAt=Date.now()}async function qc(t,{toNode:e,mode:n="transfer"}={}){if(!e||typeof e!="string")throw new Error("toNode required");let r=await K(),o=Z(r,t);if(!o)throw new Error(`track "${t}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};mp(t),await xp(t,o,n==="recover"?"track recovered onto another node":"track transferred to another node");for(let a of o.boxes)qn.has(a.state)||(a.state="stopped");let s=await Kf({id:o.id,cwd:o.cwd});return o.transferredTo=e,o.transferredAt=Date.now(),pt(o,"transferred-out",{toNode:e,mode:n,box:o.leadBox,...s.pushed?{rescueRef:s.ref,rescueSha:s.sha}:{}}),await J(),{ok:!0,toNode:e,rescue:s.pushed?{ref:s.ref,sha:s.sha}:null}}async function $p(t){let e=[];for(let n of Array.isArray(t)?t:[])if(!(!n?.id||!n?.toNode))try{let r=await qc(n.id,{toNode:n.toNode,mode:n.mode==="recover"?"recover":"transfer"});e.push({id:n.id,...r})}catch(r){/not found/i.test(String(r?.message))||console.error(`[transfer] demote of ${n.id} failed: ${r?.message||r}`)}return e}async function q0(t,e,n){try{await qi(t,e,n)}catch{await un(t,["worktree","prune"]).catch(()=>{}),await qi(t,e,n)}return e}async function Op(t={}){let{trackId:e,name:n,agent:r="claude",skipPermissions:o=!0,mode:s="transfer"}=t;if(!e||typeof e!="string"||!/^[0-9a-f]{8}$/.test(e))throw new Error("trackId (8-hex string) required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");if(s!=="transfer"&&s!=="recover")throw new Error(`unknown adopt mode "${s}"`);if(tp.has(e))throw new Error("an adopt is already running for this Track");tp.set(e,{startedAt:Date.now()});try{return await rN({...t,trackId:e,name:n,agent:r,skipPermissions:o,mode:s})}finally{tp.delete(e)}}async function rN({trackId:t,name:e,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,brief:u=null,epochs:f=[],createdAt:d=null,leadBox:p=null,reason:m=null}){let w=await xa(n,r),y=await K(),k=Z(y,t);if(k&&!k.transferredTo)throw new Error(`track "${t}" is already hosted on this node`);k&&(await Kf({id:k.id,cwd:k.cwd}).catch(()=>{}),await dx(k),y.tracks.splice(y.tracks.indexOf(k),1),await J()),await H.mkdir(ua,{recursive:!0});let x=W.join(ua,t);await H.rm(x,{recursive:!0,force:!0}).catch(()=>{});let _=null,O=!1,z=[];try{let C=[];if(i)for(let v of Array.isArray(c)?c:[]){let A=typeof v=="string"?v:v?.repo;if(!A||!/^[^/\s]+\/[^/\s]+$/.test(A))continue;let tt=await Kr(A)??await ri(A);await un(tt,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],op),C.push({slug:A,base:tt,name:typeof v=="object"?v?.name??null:null})}if(i&&C.length===1){let{base:v,slug:A}=C[0];await q0(v,x,i),z.push({base:v,dir:x}),await ha(x);let tt=await ko(v);_={dir:x,base:v,branch:i,kind:"worktree",baseRef:tt,repo:A}}else if(i&&C.length>=2){await H.mkdir(x,{recursive:!0});let v=new Set([".bostrat"]),A=[];for(let tt of C){let X=tt.name||W.basename(tt.base);v.has(X)&&(X=tt.slug.replace("/","-"));for(let Ot=2;v.has(X);Ot++)X=`${W.basename(tt.base)}-${Ot}`;v.add(X);let et=W.join(x,X);await q0(tt.base,et,i),z.push({base:tt.base,dir:et});let rr=await ko(tt.base);A.push({name:X,dir:et,base:tt.base,baseRef:rr,repo:tt.slug})}await Ta(x,A,i),_={dir:x,kind:"multi",branch:i,repos:A}}else O=!0,await H.mkdir(x,{recursive:!0}),await vd(x),await ha(x),_={dir:x,base:null,branch:null,kind:"repo"};let P=!1,T=null;if(l?.sha&&_.kind==="worktree")try{await un(x,["fetch","origin",l.ref||Wf(t)],op);let{stdout:v}=await un(x,["rev-parse","FETCH_HEAD^"]),{stdout:A}=await un(x,["rev-parse","HEAD"]);v.trim()===A.trim()?(await un(x,["read-tree","--reset","-u","FETCH_HEAD"]),await un(x,["reset","-q"]),P=!0):T="snapshot does not parent the fetched branch head"}catch(v){T=String(v?.message||v).split(`
|
|
190
|
+
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await L0({id:t,cwd:x}).catch(()=>({restored:!1}));let D=W.join(x,Pr),j=null;(await di(x)).restored?j="checkpoint":typeof u=="string"&&u.trim()&&(await H.mkdir(W.dirname(D),{recursive:!0}),await H.writeFile(D,u),j="brief");let st=new Set(y.tracks.flatMap(v=>(v.boxes||[]).map(A=>A.name))),E=null;p&&!st.has(p)&&!await ot(p).catch(()=>null)?E=p:(E=`${e.trim()} lead`,st.has(yi(E))&&(E=`${e.trim().slice(0,50).trim()} ${t} lead`));let N=await vn(E,x,{agentCmd:w,agent:n});As({cwd:N.cwd,sessionName:N.name,agent:n,agentCmd:w});let Y=Date.now(),$t=Ep(N,"lead","live"),dt=(Array.isArray(f)?f:[]).filter(v=>v&&typeof v=="object").map(v=>({...v})),g=dt[dt.length-1];g&&g.endedAt==null&&(g.endedAt=Y,g.endReason==null&&(g.endReason=o==="recover"?"node-offline":"node-transfer")),dt.push({agent:n,sessionId:N.sessionId??null,pinned:N.pinned===!0,startedAt:Y,endedAt:null}),$t.epochs=dt;let h={id:t,name:e.trim(),cwd:N.cwd,workspace:_,leadBox:N.name,boxes:[$t],events:[],createdAt:Number.isFinite(d)?d:Y,...j?{pendingHandoffHydrate:{artifact:Pr,at:Y}}:{}};return pt(h,"transferred-in",{box:N.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,branch:_.branch??null,wip:P,epoch:dt.length-1,name:h.name,cwd:h.cwd,workspace:{base:_.base??null,branch:_.branch??null,kind:_.kind,..._.baseRef?{baseRef:_.baseRef}:{},..._.repo?{repo:_.repo}:{},..._.repos?{repos:_.repos.map(({name:v,base:A,baseRef:tt,repo:X})=>({name:v,base:A,baseRef:tt,repo:X}))}:{}}}),y.tracks.push(h),await J(),{...await Le(h),adopt:{wipApplied:P,wipSkipped:T,codeless:O,artifactSource:j}}}catch(C){for(let P of z)await ne(P.base,P.dir).catch(()=>{});throw await H.rm(x,{recursive:!0,force:!0}).catch(()=>{}),C}}async function wx(t,e,n){let r=await K(),o=Z(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await J())}async function jo(t,e){t.step=e,await Aa(t)}function gx(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 yx(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function iN(t,e,n){if(!yx(n)||oe.has(e)||!await ot(n.box).catch(()=>null))return!1;try{await H.access(n.dir)}catch{return!1}let o=await ot(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return oe.set(e,s),kx(s,o).catch(a=>bx(s,a)),!0}async function Cp(){let t=await K(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(oe.has(r))continue;if(await iN(n,r,o)){e=!0,pt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await G({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=gx(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!qn.has(i.state)&&(i.state="canceled"),pt(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await G({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await K0(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||qn.has(r.state)||oe.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?W.join(qo,`${n.id}-${r.subtaskId}`):null;await G({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await K0(n,r.name,o)}}return e=await cN(t)||e,e&&await J(),e}async function K0(t,e,n){if(e&&await Ie(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of se(t.workspace)){let s=W.join(n,o.name);await ne(o.base,s).then(()=>!0,()=>!1)||await ne(o.dir,s).catch(()=>{})}await H.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ot(t.leadBox).catch(()=>null);r?.cwd&&await ne(r.cwd,n).catch(()=>{})}async function cN(t){let e;try{e=await H.readdir(qo)}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 oe.values())o.dir&&n.add(W.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=W.join(qo,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of se(a.workspace))await ne(i.base,W.join(s,i.name)).catch(()=>{});await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ot(a.leadBox).catch(()=>null):null;i?.cwd?await ne(i.cwd,s).catch(()=>{}):await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await G({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Np(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!go(n))throw new Error("task (string) required");let s=await xa(e,r),a=vr(e);if(a){let p=new Error(`can't delegate to ${e} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=Z(await K(),t);if(!i)throw new Error(`track "${t}" not found`);let c=await ot(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let p=se(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of p)if(!m?.dir||!await Wi(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);u=p.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await Wi(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=ya().slice(0,8),d={id:f,trackId:t,trackName:i.name,agent:e,task:go(n),skipPermissions:r,reason:X0(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Mp(i.name)}-${f}`,base:null,repos:u,dir:W.join(qo,`${t}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return oe.set(f,d),await Aa(d),lN(d,c,s).catch(p=>bx(d,p)),d}async function bx(t,e){t.failedStep=t.step,t.error=e instanceof ga?"canceled":e?.message||String(e),t.step=e instanceof ga?"canceled":"error",t.finishedAt=Date.now(),await Bp(t).catch(()=>{}),await Aa(t).catch(()=>{}),await pN(t).catch(()=>{})}function Gc(t){let e=oe.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 Pp(t){let e=oe.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 lN(t,e,n){if(await jo(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await yo(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await Aa(t)}else t.base=await yo(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await jo(t,"spawn"),await H.mkdir(qo,{recursive:!0}),t.repos){await H.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await bo(a.leadDir,W.join(t.dir,a.name),t.branch,a.base);let s=se((Z(await K(),t.trackId)||{}).workspace);await Ta(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 bo(e.cwd,t.dir,t.branch,t.base),await ha(t.dir);await uN(t,e);let r=t.trackName.slice(0,38).trim(),o=await vn(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await dN(t,o),await Z0(t.trackId,o.name,fM(t.task,!!t.repos),{kind:"subtask"}),await kx(t,e)}async function kx(t,e){await jo(t,"working");let n=W.join(t.dir,Oc);if(await hN(n,t),t.canceled)throw new ga;if(await jo(t,"merge"),t.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of t.repos){let u=W.join(t.dir,l.name);await kd(u,`bostrat: subtask ${t.id} result (auto-committed)`),l.stat=await Ad(l.leadDir,l.base,t.branch,Do),l.files=await Ed(l.leadDir,l.base,t.branch,Do);let f=await _d(l.leadDir,l.base,t.branch,Do);l.applied=f.applied,l.conflicts=f.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(d=>W.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:W.join(l.name,d))),f.applied||(c=!1)}t.stat=s,t.files=a,t.conflicts=i,t.applied=c}else{await kd(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Ad(e.cwd,t.base,t.branch,Do),t.files=await Ed(e.cwd,t.base,t.branch,Do);let s=await _d(e.cwd,t.base,t.branch,Do);t.applied=s.applied,t.conflicts=s.conflicts}await Aa(t);let r=await mN(t,e,n);await jo(t,"resume"),await Z0(t.trackId,t.leadBox,pM(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await jo(t,"reap"),await Bp(t),t.step="done";let o=Date.now();await wx(t.trackId,t.id,{...Kc(t),finishedAt:o}),await fN(t,o),t.finishedAt=o}async function uN(t,e){let n=W.join(t.dir,W.dirname(da));await H.mkdir(n,{recursive:!0});let r="";try{r=await Zh(e.cwd,e.sessionId)}catch{}if(!r)try{r=await H.readFile(W.join(e.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${t.id}
|
|
185
191
|
|
|
186
192
|
**Delegated task:** ${t.task}
|
|
187
193
|
|
|
@@ -192,15 +198,15 @@ ${a}`}async function Rc(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name
|
|
|
192
198
|
## Conversation so far (lead \u2192 you)
|
|
193
199
|
|
|
194
200
|
${r}
|
|
195
|
-
`;await
|
|
201
|
+
`;await H.writeFile(W.join(t.dir,da),o)}async function dN(t,e){let n=await K(),r=Z(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:ss(e.hookToken),createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),pt(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]||{},...Kc(t)},await J())}async function fN(t,e=t.finishedAt){let n=await K(),r=Z(n,t.trackId);if(!r)return;let o=r.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=t.conflicts.length?"conflict":"merged",o.stat=t.stat),pt(r,"subtask-merged",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,stat:t.stat,conflicts:t.conflicts.length,files:t.files,durationMs:e?e-t.startedAt:null}),await J()}async function pN(t){let e=await K(),n=Z(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),pt(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]||{},...Kc(t)},await J()}async function mN(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=W.join(e.cwd,r);await H.mkdir(W.dirname(o),{recursive:!0}),await H.copyFile(n,o)}catch{}return r}async function Bp(t){if(t.box&&await Ie(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await ne(e.leadDir,W.join(t.dir,e.name)).catch(()=>{});await H.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await ot(t.leadBox).catch(()=>null))?.cwd;n&&await ne(n,t.dir).catch(()=>{})}}async function hN(t,e){let n=(e.startedAt||Date.now())+oN;for(;;){if(e.canceled||e.forceMerge)return;try{await H.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await Kn(sN)}}function Kn(t){return new Promise(e=>setTimeout(e,t))}var G0,dn,V0,lM,ua,uM,Mc,ba,Wn,Pr,J0,dM,Oc,da,fM,pM,fe,zf,Z,Jf,pt,Yf,hM,qn,U0,yM,bM,kM,xM,TM,fa,H0,EM,_M,IM,RM,$M,Lo,pa,NM,BM,DM,LM,Xf,UM,cx,ma,lx,KM,la,Zf,GM,Qf,YM,wa,XM,ZM,W0,Ep,Wo,Ko,op,un,sp,Ip,tp,oN,sN,qo,Do,oe,aN,Dr,Kc,Aa,Mp,ga,Ir=M(()=>{Wt();zu();Pg();Ii();yn();uu();Lg();zt();qe();is();vi();ny();co();hd();gd();by();Ou();Ti();Uu();ge();Id();sr();Es();zi();Gy();Pd();Gb();Wu();Bu();zb();Qd();ut();gc();lc();To();Af();No();Pf();Rc();Hf();jn();N0();Gf();F0();fi();G0=cM(iM),dn=process.env.BOSTRAT_STATE_DIR||W.join(process.env.HOME||"/tmp",".bostrat"),V0=W.join(dn,"tracks.json"),lM=W.join(dn,"events.jsonl"),ua=W.join(dn,"workspaces"),uM=process.env.BOSTRAT_DEFAULT_REPO||null,Mc={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"}},ba=Object.keys(Mc);Wn={at:0,value:null};Pr=".bostrat/handoff/latest.md",J0=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;dM=/\(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\.\)$/;Oc=".bostrat/handoff/subtask-result.md",da=".bostrat/handoff/lead-context.md",fM=(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 ${da} \u2014 read that first for context. Your scoped task:
|
|
196
202
|
|
|
197
203
|
${t}
|
|
198
204
|
|
|
199
|
-
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 ${
|
|
205
|
+
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 ${Oc} (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 ${da} \u2014 read that first for context. Your scoped task:
|
|
200
206
|
|
|
201
207
|
${t}
|
|
202
208
|
|
|
203
|
-
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 ${
|
|
209
|
+
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 ${Oc} 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.`,pM=(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."),fe=null,zf=null;Z=(t,e)=>t.tracks.find(n=>n.id===e)||null,Jf=Promise.resolve();pt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),mM(t.id,r),Jy(t,r),r};nw(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await K()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(pt(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await J().catch(()=>{}))});Yf=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),hM=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);qn=new Set(["merged","conflict","canceled","failed","handed-off"]),U0=new Map;yM=5e3;bM={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};kM=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];xM=10*6e4;TM=1500,fa=new Map;H0="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.";EM=4e3,_M=3,IM=[300,350,400],RM=1e3,$M=12,Lo=new Map,pa=new Map;NM=[250,400,500,600];BM=3e4,DM=[350,650,900];LM=[250,450,700],Xf=2;UM=12e4;cx=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}))});ma="# bostrat workspace",lx=`${ma}
|
|
204
210
|
|
|
205
211
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
206
212
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -430,69 +436,68 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
430
436
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
431
437
|
implementation turn this way, every time. When the operator has already asked you
|
|
432
438
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
433
|
-
`,
|
|
434
|
-
`)),
|
|
435
|
-
`,`${
|
|
439
|
+
`,KM=lx,la=lx+Xw;Zf=t=>typeof t=="string"&&(t===ma||t.startsWith(`${ma}
|
|
440
|
+
`)),GM="# bostrat multi-repo workspace";Qf=la.replace(`${ma}
|
|
441
|
+
`,`${ma}
|
|
436
442
|
|
|
437
443
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
438
444
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
439
445
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
440
446
|
When working anywhere else, ignore everything below.
|
|
441
|
-
`);bM=new Set(["claude","codex","cursor"]);aa=new Set,kM=t=>aa.has(t);xM=9e4;y0={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};H0=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:Yo(t.hookToken),createdAt:Date.now()});ia=new Map,No=t=>ia.get(t)||(ue?Q(ue,t)?.handoff:null)||null;AM=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,EM=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Po=H.join(ln,"worktrees"),Io=[na,yc],de=new Map,_M=t=>de.get(t)||(ue?ue.tracks.map(e=>e.subtasks?.[t]).find(Boolean):null)||null,$r=t=>{let e=new Map([...de.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=ue?Q(ue,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)},Cc=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});ha=t=>q0(t.trackId,t.id,Cc(t));lp=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",ca=class extends Error{}});Ht();import ut from"node:path";import Ft from"node:fs";import k1 from"node:readline";import{fileURLToPath as x1}from"node:url";import ve from"express";import S1 from"node:http";import v1 from"node:https";import ym from"node:os";import{WebSocketServer as T1}from"ws";Ht();import $A from"node-pty";async function Sh(t,e,n={}){if(!bt(e)){ci(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await xn()){ci(t,`host tmux server not running on ${te}`),t.close(1011,"tmux server unavailable");return}if(!await tt(e)){ci(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=xh(n.cols,120,vh),o=xh(n.rows,32,Th),s;try{s=$A.spawn(B,["-S",te,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ci(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Vl(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Vl(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"&&PA(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{}MA(e)})}var OA=6,CA=250;async function MA(t,e=OA,n=CA){for(let r=0;r<e;r++){if(await sr(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var vh=20,Th=6;function PA(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=vh&&Number(e)>=Th}function xh(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Vl(t,e){try{t.send(JSON.stringify(e))}catch{}}function ci(t,e){Vl(t,{type:"error",message:e})}function Ah(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}}we();li();Du();xr();var DM=t=>!!t&&!t.finishedAt&&!t.canceled&&!t.error;function Y0({tracks:t=[],views:e=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of t)!o||o.archivedAt&&!o.archiving||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(DM)&&r.push(`merge-back:${o.id}`));return e.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}ct();Qt();var jM=(...t)=>fetch(...t);function LM(t=process.env){return!!(vt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function pp(t=process.env){if(!LM(t))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let e=new AbortController,n=setTimeout(()=>e.abort(),1e4);try{let r=await jM(`${vt(t)}/v1/circuits`,{headers:{authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN}`},signal:e.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function Bo(t,e=process.env){let n=String(t??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await pp(e),o=r.find(c=>c.id===n);if(o)return o;let s=n.toLowerCase(),a=r.filter(c=>c.name.toLowerCase()===s);if(a.length===1)return a[0];if(a.length>1)throw new Error(`"${n}" names ${a.length} circuits \u2014 use an id (bostrat-circuit ls)`);let i=r.filter(c=>c.name.toLowerCase().includes(s)).slice(0,5);throw new Error(i.length?`no circuit named "${n}" \u2014 near matches: ${i.map(c=>`${c.name} (${c.id})`).join(", ")}`:`no circuit named "${n}" \u2014 create it on the Circuits page first (bostrat-circuit ls lists yours)`)}async function Pc(t,e,{source:n="chat"}={}){return I(t,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:e?{circuitId:e.id,circuitName:e.name??null,source:n}:{circuitId:null,source:n}})}ct();Qt();var FM=(...t)=>fetch(...t);async function Nc(t,e,n,r=process.env){if(!(vt(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await FM(`${vt(r)}${e}`,{method:t,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function UM(t,e=process.env){return Nc("POST","/v1/circuits",{name:t},e).then(n=>n.circuit)}async function X0({circuitRef:t,newCircuitName:e,name:n,markdown:r},o=process.env){let s=e?await UM(e,o):await Bo(t,o),a=await Nc("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function mp(t,e=process.env){let n=await Bo(t,e),r=await Nc("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,e);return{circuit:n,...r}}async function hp(t,e=process.env){return(await Nc("GET","/v1/circuits",null,e)).memberships?.[t]??null}async function Z0(t,e,n,r=process.env){let{blueprint:o,items:s}=await mp(e,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await I(t,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}$f();ct();import xa from"node:fs/promises";import qc from"node:path";import OP from"node:os";gn();import HM from"node:fs/promises";import Q0 from"node:path";import WM from"node:os";var tx=()=>Q0.join(process.env.BOSTRAT_STATE_DIR||Q0.join(process.env.HOME||WM.tmpdir(),".bostrat"),"integrations.json");async function dn(){try{return JSON.parse(await HM.readFile(tx(),"utf8"))||{}}catch{return{}}}var Bc=["linked","prOpened","prMerged","create"],wp=["prompt","auto","off"],qM={create:["prompt","off"]},ex=t=>qM[t]??wp;function Dc(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of Bc){let s=ex(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 Or(){let t=await dn(),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:Dc(t),defaultTeam:n}:null}async function jc(){let t=await dn(),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:Dc(t)}}async function gp(){return Dc(await dn())}async function nx(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${Bc.join(", ")}`);for(let[r,o]of e){if(!Bc.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Bc.join(", ")}`);let s=ex(r);if(!s.includes(o))throw new Error(s.length<wp.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${wp.join(", ")}`)}let n=await dn();return n.ticketFlow={...Dc(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await zt(tx(),n,{mode:384}),n.ticketFlow}async function wa(){let t=(await dn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function KM(){let t=(await dn()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var GM={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Lc(){let e=(await dn()).voiceChat||{},n=e.provider||"openai",r=GM[n];if(!r)return null;let o=n==="openai"?await KM():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function rx(){let t=(await dn()).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 Cr(){let t=(await dn()).slack||{},e=process.env.BOSTRAT_SLACK_BOT_TOKEN||t.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||t.appToken;return!e||!n?null:{botToken:e,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||t.channel||null,maxConcurrent:Number.isInteger(t.maxConcurrent)&&t.maxConcurrent>0?t.maxConcurrent:2}}ct();bn();ur();Ao();import ya from"node:fs/promises";import fx from"node:path";import gP from"node:os";import{randomUUID as px}from"node:crypto";var bp={};gl(bp,{createComment:()=>eP,findIssueUrl:()=>YM,issueStates:()=>oP,mdToAdf:()=>ax,moveIssue:()=>sP,parseIssueRef:()=>JM,resolveIssue:()=>XM,uploadImage:()=>nP,upsertCard:()=>ZM,whoami:()=>yp});var Do=t=>String(t.baseUrl||"").replace(/\/$/,""),sx=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Mr(t,e,n,r){let o=await fetch(`${Do(t)}${n}`,{method:e,headers:{authorization:sx(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 VM=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,zM=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function JM(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(VM);return r?r[1].toUpperCase():null}let n=e.match(zM);return n?n[1]:null}function YM(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 XM(t,e){let n;try{n=await Mr(t,"GET",`/rest/api/3/issue/${encodeURIComponent(e)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${e}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${Do(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function ZM(t,e,{url:n,title:r,subtitle:o}){await Mr(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 QM=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function tP(t){let e=[],n=0;for(let r of t.matchAll(QM)){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 ax(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
442
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
447
|
+
`);YM=new Set(["claude","codex","cursor"]);wa=new Set,XM=t=>wa.has(t);ZM=9e4;W0={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Ep=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:ss(t.hookToken),createdAt:Date.now()});Wo=new Map,Ko=t=>Wo.get(t)||(fe?Z(fe,t)?.handoff:null)||null;op=6e4,un=(t,e,n=15e3)=>G0("git",["-C",t,...e],{timeout:n,maxBuffer:8*1024*1024}),sp=new Map,Ip=t=>sp.get(t)||null;tp=new Map;oN=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,sN=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,qo=W.join(dn,"worktrees"),Do=[da,Oc],oe=new Map,aN=t=>oe.get(t)||(fe?fe.tracks.map(e=>e.subtasks?.[t]).find(Boolean):null)||null,Dr=t=>{let e=new Map([...oe.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=fe?Z(fe,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)},Kc=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});Aa=t=>wx(t.trackId,t.id,Kc(t));Mp=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",ga=class extends Error{}});Wt();import mt from"node:path";import Ft from"node:fs";import K1 from"node:readline";import{fileURLToPath as G1}from"node:url";import me from"express";import V1 from"node:http";import z1 from"node:https";import Hm from"node:os";import{WebSocketServer as J1}from"ws";Wt();import ZA from"node-pty";async function Kh(t,e,n={}){if(!St(e)){Si(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await Sn()){Si(t,`host tmux server not running on ${ee}`),t.close(1011,"tmux server unavailable");return}if(!await ot(e)){Si(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=qh(n.cols,120,Gh),o=qh(n.rows,32,Vh),s;try{s=ZA.spawn(L,["-S",ee,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Si(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&nu(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(nu(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"&&nE(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{}eE(e)})}var QA=6,tE=250;async function eE(t,e=QA,n=tE){for(let r=0;r<e;r++){if(await ur(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Gh=20,Vh=6;function nE(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=Gh&&Number(e)>=Vh}function qh(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function nu(t,e){try{t.send(JSON.stringify(e))}catch{}}function Si(t,e){nu(t,{type:"error",message:e})}function zh(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}}ge();vi();zu();Ir();var wN=t=>!!t&&!t.finishedAt&&!t.canceled&&!t.error;function Sx({tracks:t=[],views:e=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of t)!o||o.archivedAt&&!o.archiving||o.transferredTo||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(wN)&&r.push(`merge-back:${o.id}`));return e.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}ut();zt();var gN=(...t)=>fetch(...t);function yN(t=process.env){return!!(bt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function Dp(t=process.env){if(!yN(t))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let e=new AbortController,n=setTimeout(()=>e.abort(),1e4);try{let r=await gN(`${bt(t)}/v1/circuits`,{headers:{authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN}`},signal:e.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function Go(t,e=process.env){let n=String(t??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Dp(e),o=r.find(c=>c.id===n);if(o)return o;let s=n.toLowerCase(),a=r.filter(c=>c.name.toLowerCase()===s);if(a.length===1)return a[0];if(a.length>1)throw new Error(`"${n}" names ${a.length} circuits \u2014 use an id (bostrat-circuit ls)`);let i=r.filter(c=>c.name.toLowerCase().includes(s)).slice(0,5);throw new Error(i.length?`no circuit named "${n}" \u2014 near matches: ${i.map(c=>`${c.name} (${c.id})`).join(", ")}`:`no circuit named "${n}" \u2014 create it on the Circuits page first (bostrat-circuit ls lists yours)`)}async function Vc(t,e,{source:n="chat"}={}){return $(t,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:e?{circuitId:e.id,circuitName:e.name??null,source:n}:{circuitId:null,source:n}})}ut();zt();var bN=(...t)=>fetch(...t);async function zc(t,e,n,r=process.env){if(!(bt(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await bN(`${bt(r)}${e}`,{method:t,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function kN(t,e=process.env){return zc("POST","/v1/circuits",{name:t},e).then(n=>n.circuit)}async function vx({circuitRef:t,newCircuitName:e,name:n,markdown:r},o=process.env){let s=e?await kN(e,o):await Go(t,o),a=await zc("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function jp(t,e=process.env){let n=await Go(t,e),r=await zc("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,e);return{circuit:n,...r}}async function Lp(t,e=process.env){return(await zc("GET","/v1/circuits",null,e)).memberships?.[t]??null}async function Tx(t,e,n,r=process.env){let{blueprint:o,items:s}=await jp(e,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await $(t,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}Hf();ut();import Oa from"node:fs/promises";import rl from"node:path";import uP from"node:os";yn();import xN from"node:fs/promises";import Ax from"node:path";import SN from"node:os";var Ex=()=>Ax.join(process.env.BOSTRAT_STATE_DIR||Ax.join(process.env.HOME||SN.tmpdir(),".bostrat"),"integrations.json");async function fn(){try{return JSON.parse(await xN.readFile(Ex(),"utf8"))||{}}catch{return{}}}var Jc=["linked","prOpened","prMerged","create"],Fp=["prompt","auto","off"],vN={create:["prompt","off"]},_x=t=>vN[t]??Fp;function Yc(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of Jc){let s=_x(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 jr(){let t=await fn(),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:Yc(t),defaultTeam:n}:null}async function Xc(){let t=await fn(),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:Yc(t)}}async function Up(){return Yc(await fn())}async function Ix(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${Jc.join(", ")}`);for(let[r,o]of e){if(!Jc.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Jc.join(", ")}`);let s=_x(r);if(!s.includes(o))throw new Error(s.length<Fp.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Fp.join(", ")}`)}let n=await fn();return n.ticketFlow={...Yc(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Jt(Ex(),n,{mode:384}),n.ticketFlow}async function Ea(){let t=(await fn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function TN(){let t=(await fn()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var AN={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Zc(){let e=(await fn()).voiceChat||{},n=e.provider||"openai",r=AN[n];if(!r)return null;let o=n==="openai"?await TN():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function Rx(){let t=(await fn()).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 Lr(){let t=(await fn()).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}}ut();kn();hr();No();import Ia from"node:fs/promises";import jx from"node:path";import YN from"node:os";import{randomUUID as Lx}from"node:crypto";var Wp={};Rl(Wp,{createComment:()=>NN,findIssueUrl:()=>RN,issueStates:()=>DN,mdToAdf:()=>Cx,moveIssue:()=>jN,parseIssueRef:()=>IN,resolveIssue:()=>$N,uploadImage:()=>PN,upsertCard:()=>ON,whoami:()=>Hp});var Vo=t=>String(t.baseUrl||"").replace(/\/$/,""),Ox=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Fr(t,e,n,r){let o=await fetch(`${Vo(t)}${n}`,{method:e,headers:{authorization:Ox(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 EN=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,_N=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function IN(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(EN);return r?r[1].toUpperCase():null}let n=e.match(_N);return n?n[1]:null}function RN(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 $N(t,e){let n;try{n=await Fr(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:`${Vo(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function ON(t,e,{url:n,title:r,subtitle:o}){await Fr(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 CN=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function MN(t){let e=[],n=0;for(let r of t.matchAll(CN)){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 Cx(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
448
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...MN(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 NN(t,e,n){let r=await Fr(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Cx(n)});return r?.id?`${Vo(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function Hp(t){let e=await Fr(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Vo(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function PN(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(`${Vo(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:Ox(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??`${Vo(t)}/rest/api/3/attachment/content/${c.id}`}var BN={new:"unstarted",indeterminate:"started",done:"completed"},$x=t=>BN[t?.key]??"started";async function DN(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([Fr(t,"GET",`/rest/api/3/issue/${n}?fields=status`),Fr(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:$x(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:$x(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function jN(t,e,n){await Fr(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var Vp={};Rl(Vp,{createComment:()=>GN,createIssue:()=>Gp,createProject:()=>Kp,findIssueUrl:()=>WN,issueStates:()=>zN,moveIssue:()=>JN,parseIssueRef:()=>HN,resolveIssue:()=>qN,teams:()=>zo,uploadImage:()=>VN,upsertCard:()=>KN,whoami:()=>qp});var LN=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Fe(t,e,n={}){let r=await fetch(LN(),{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 FN=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,UN=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function HN(t){let e=String(t||"").trim(),n=e.match(FN);if(n)return n[1].toUpperCase();let r=e.match(UN);return r?r[1]:null}function WN(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 qN(t,e){let r=(await Fe(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 KN(t,e,{url:n,title:r,subtitle:o}){let s=await Fe(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 GN(t,e,n){let r=await Fe(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 qp(t){let e=await Fe(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 VN(t,{filename:e,contentType:n,buffer:r}){let o=await Fe(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
443
449
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
444
450
|
success
|
|
445
451
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
446
452
|
}
|
|
447
|
-
}`,{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 hP(t,e){let n=await Le(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 wP(t,e,n){if(!(await Le(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 jo(t){return((await Le(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var ix=255;async function xp(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>ix,a=s?`${o.slice(0,ix-1).trimEnd()}\u2026`:o,i=await Le(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 Sp(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Le(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 mx={linear:{api:vp,config:Or},jira:{api:bp,config:jc}};async function $p(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(mx)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function Pr(t){let e=mx[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function Op(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 hx=()=>process.env.BOSTRAT_STATE_DIR||fx.join(process.env.HOME||gP.tmpdir(),".bostrat"),_p=()=>fx.join(hx(),"tickets.json"),wx=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Fc=t=>`${wx()}/track/${t}`,Hn=null,Tp=null,Ap=Promise.resolve();async function ka(){return Hn||(Tp??=ya.readFile(_p(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(Hn=t,!e&&wt()){let{records:n,source:r}=await $t("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;Hn[s]=i}Object.keys(Hn).length&&await Ip().catch(()=>{})}}return Hn}).finally(()=>Tp=null),Tp)}async function Ip(){let t=JSON.stringify({tickets:Hn},null,2),e=`${_p()}.${process.pid}.tmp`;await ya.mkdir(hx(),{recursive:!0}),await ya.writeFile(e,t),await ya.rename(e,_p())}async function Wn(t=null){if(Ap=Ap.then(Ip,Ip),await Ap.catch(()=>{}),!t)return;let e=Hn?.[t];e?Ut("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):me("tickets",t).catch(()=>{})}async function Fe(t){return(await ka())[t]??null}async function gx(){return{...await ka()}}async function yx(){return(await $p()).length>0}async function Lo(t,e,{source:n="api"}={}){let r=await $p();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(e);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null,...t.workspace?.kind==="multi"?{repos:(t.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await ka())[t.id]=c,await Wn(t.id),await I({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await ba(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 ga(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await xx(t.id,c,"linked"),c}async function Cp(t){let e=await ka();return e[t]?(delete e[t],await Wn(t),!0):!1}var Rp=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function bx(t,e){if(!t.track?.cwd)return null;let n=Rn(await L({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function yP(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Rp(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${Rp(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function bP(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(Rp(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function ba(t,e){let n=await Pr(e);if(!n)return;let r=await bx(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:Fc(t),title:`Bostrat Track: ${e.track.name}`,subtitle:bP(e,r)})}function Uc(t){let e=new URL(wx());return`${e.protocol}//${t}.${e.host}/`}var kP={linked:"linked",opened:"prOpened",merged:"prMerged"};function xP(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 kx(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Wn(t),await I({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function xx(t,e,n){let r=await Pr(e);if(!r)return;let o=r.cfg.ticketFlow?.[kP[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=Op(s,a),c=xP(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 kx(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await I({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:px(),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 Sx(t){let e=await Fe(t);if(!e)throw new Error("no ticket linked to this Track");let n=await Pr(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=Op(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await Wn(t)),{key:e.key,url:e.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function Mp(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await Fe(t);if(!r)throw new Error("no ticket linked to this Track");let o=await Pr(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Op(s,a),c=a.find(u=>u.id===e);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Wn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await kx(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function vx(t,e){let n=await Fe(t);if(!n)throw new Error("no ticket linked to this Track");let r=await Pr(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await I({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function Tx(t,e,n){if(!e)throw new Error("offerId required");let r=await L({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await Mp(t.id,o.payload.to?.id,{mode:"offer"})),await I({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var cx=50;async function Ax(){let t=await Or();if(!t)throw new Error("no ticketing integration configured");return jo(t)}function SP(t){let e=t&&typeof t=="object"?t:{},n=(Array.isArray(e.issues)?e.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=e.epic&&String(e.epic.name||"").trim()?{name:String(e.epic.name).trim(),description:e.epic.description?String(e.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>cx)throw new Error(`too many issues (${n.length}); cap is ${cx} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function vP(t,e,n,r){let o=r??await jo(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 Fe(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 Ex(t,e){let n=await Or();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=SP(e),o=await jo(n),s=await vP(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=px();return await I({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function _x(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await L({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await I({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await Or();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await jo(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let m=await xp(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let w=await Sp(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}await I({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await I({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let f=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&f.length===1&&!await Fe(t.id)&&(p=await Lo(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function ga(t,e){let n=await Pr(t);n&&await n.api.createComment(n.cfg,t,e)}async function TP(t,e,n){let r=await Pr(t);if(!r)return null;let o=await en(e);if(!o)return null;let s=ke(o,n);if(!s)return null;let a=await ya.readFile(le(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 lx=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Ep=new Map;function AP(t,e){if(lx()<=0)return ba(t,e);if(Ep.has(t))return;let n=setTimeout(()=>{Ep.delete(t),Fe(t).then(r=>r?ba(t,r):null).catch(()=>{})},lx());n.unref?.(),Ep.set(t,n)}var ux=new Map;async function dx(t,e,n){let r=ux.get(t)??0;Date.now()-r<600*1e3||(ux.set(t,Date.now()),await I({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function EP(t,{resolveTrack:e=null}={}){let n=await Fe(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await $p())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 Lo(s,o,{source:"chat"})}catch(a){await dx(t.trackId,s.cwd,a)}return}if(!n.track.cwd&&e){let o=await e(t.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Wn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Wn(t.trackId),await ba(t.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Wn(t.trackId),await ba(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await bx(n,t.trackId).catch(()=>null):null;await ga(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
453
|
+
}`,{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 zN(t,e){let n=await Fe(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 JN(t,e,n){if(!(await Fe(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 zo(t){return((await Fe(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var Mx=255;async function Kp(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>Mx,a=s?`${o.slice(0,Mx-1).trimEnd()}\u2026`:o,i=await Fe(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 Gp(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Fe(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 Fx={linear:{api:Vp,config:jr},jira:{api:Wp,config:Xc}};async function tm(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(Fx)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function Ur(t){let e=Fx[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function em(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 Ux=()=>process.env.BOSTRAT_STATE_DIR||jx.join(process.env.HOME||YN.tmpdir(),".bostrat"),Xp=()=>jx.join(Ux(),"tickets.json"),Hx=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Qc=t=>`${Hx()}/track/${t}`,Gn=null,zp=null,Jp=Promise.resolve();async function $a(){return Gn||(zp??=Ia.readFile(Xp(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(Gn=t,!e&&kt()){let{records:n,source:r}=await Ct("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;Gn[s]=i}Object.keys(Gn).length&&await Zp().catch(()=>{})}}return Gn}).finally(()=>zp=null),zp)}async function Zp(){let t=JSON.stringify({tickets:Gn},null,2),e=`${Xp()}.${process.pid}.tmp`;await Ia.mkdir(Ux(),{recursive:!0}),await Ia.writeFile(e,t),await Ia.rename(e,Xp())}async function Vn(t=null){if(Jp=Jp.then(Zp,Zp),await Jp.catch(()=>{}),!t)return;let e=Gn?.[t];e?Ht("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):he("tickets",t).catch(()=>{})}async function Ue(t){return(await $a())[t]??null}async function Wx(){return{...await $a()}}async function qx(){return(await tm()).length>0}async function Jo(t,e,{source:n="api"}={}){let r=await tm();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 $a())[t.id]=c,await Vn(t.id),await $({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Ra(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 _a(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Vx(t.id,c,"linked"),c}async function nm(t){let e=await $a();return e[t]?(delete e[t],await Vn(t),!0):!1}var Qp=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Kx(t,e){if(!t.track?.cwd)return null;let n=On(await U({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function XN(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Qp(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${Qp(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function ZN(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(Qp(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Ra(t,e){let n=await Ur(e);if(!n)return;let r=await Kx(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:Qc(t),title:`Bostrat Track: ${e.track.name}`,subtitle:ZN(e,r)})}function tl(t){let e=new URL(Hx());return`${e.protocol}//${t}.${e.host}/`}var QN={linked:"linked",opened:"prOpened",merged:"prMerged"};function tP(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 Gx(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Vn(t),await $({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Vx(t,e,n){let r=await Ur(e);if(!r)return;let o=r.cfg.ticketFlow?.[QN[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=em(s,a),c=tP(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 Gx(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await $({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Lx(),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 zx(t){let e=await Ue(t);if(!e)throw new Error("no ticket linked to this Track");let n=await Ur(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=em(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await Vn(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 rm(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await Ue(t);if(!r)throw new Error("no ticket linked to this Track");let o=await Ur(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=em(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 Vn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Gx(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Jx(t,e){let n=await Ue(t);if(!n)throw new Error("no ticket linked to this Track");let r=await Ur(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await $({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function Yx(t,e,n){if(!e)throw new Error("offerId required");let r=await U({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 rm(t.id,o.payload.to?.id,{mode:"offer"})),await $({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var Nx=50;async function Xx(){let t=await jr();if(!t)throw new Error("no ticketing integration configured");return zo(t)}function eP(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>Nx)throw new Error(`too many issues (${n.length}); cap is ${Nx} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function nP(t,e,n,r){let o=r??await zo(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 Ue(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 Zx(t,e){let n=await jr();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=eP(e),o=await zo(n),s=await nP(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Lx();return await $({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function Qx(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await U({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await $({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await jr();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await zo(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let m=await Kp(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let w=await Gp(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}await $({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await $({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await Ue(t.id)&&(p=await Jo(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function _a(t,e){let n=await Ur(t);n&&await n.api.createComment(n.cfg,t,e)}async function rP(t,e,n){let r=await Ur(t);if(!r)return null;let o=await nn(e);if(!o)return null;let s=Se(o,n);if(!s)return null;let a=await Ia.readFile(de(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 Px=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Yp=new Map;function oP(t,e){if(Px()<=0)return Ra(t,e);if(Yp.has(t))return;let n=setTimeout(()=>{Yp.delete(t),Ue(t).then(r=>r?Ra(t,r):null).catch(()=>{})},Px());n.unref?.(),Yp.set(t,n)}var Bx=new Map;async function Dx(t,e,n){let r=Bx.get(t)??0;Date.now()-r<600*1e3||(Bx.set(t,Date.now()),await $({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function sP(t,{resolveTrack:e=null}={}){let n=await Ue(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await tm())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=e?await e(t.trackId):null;if(!s)return;try{await Jo(s,o,{source:"chat"})}catch(a){await Dx(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 Vn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Vn(t.trackId),await Ra(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 Vn(t.trackId),await Ra(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Kx(n,t.trackId).catch(()=>null):null;await _a(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
448
454
|
|
|
449
|
-
Agent cost for this Track: ${
|
|
455
|
+
Agent cost for this Track: ${XN(o)}.`:"")),await Vx(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await rP(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await _a(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
450
456
|
|
|
451
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
452
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
453
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
457
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await _a(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 _a(n,`**Bostrat** \u2014 live preview **${r.name}**: ${tl(r.slug)}
|
|
458
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await oP(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Dx(t.trackId,s?.cwd??null,o)}}function tS({resolveTrack:t}){Xt.on("event",e=>{sP(e,{resolveTrack:t}).catch(()=>{})})}async function eS(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ct("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await $a(),r=0;for(let[o,s]of Object.entries(n))await Ht("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}To();No();import aP from"ws";var nS=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function el(t,e,n={}){let r=await fetch(`${nS()}/${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 om=(t,e)=>el("chat.postMessage",t,e),Hr=(t,e)=>el("chat.update",t,e);async function Gt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function iP(t,e,n={}){let r=await fetch(`${nS()}/${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 rS(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await iP("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 el("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var cP=2200;function oS({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 el("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new aP(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=w=>{try{o.send(JSON.stringify(w?{envelope_id:f.envelope_id,payload:w}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},cP);Promise.resolve().then(()=>e(f)).then(w=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var iS=()=>process.env.BOSTRAT_STATE_DIR||rl.join(process.env.HOME||uP.tmpdir(),".bostrat"),lm=()=>rl.join(iS(),"slack.json"),nl=null,sm=null,am=Promise.resolve();async function dm(){return nl||(sm??=Oa.readFile(lm(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>nl=t).finally(()=>sm=null),sm)}async function sS(){let t=JSON.stringify({threads:nl},null,2),e=`${lm()}.${process.pid}.tmp`;await Oa.mkdir(iS(),{recursive:!0}),await Oa.writeFile(e,t),await Oa.rename(e,lm())}async function ve(){am=am.then(sS,sS),await am.catch(()=>{})}async function fm(t){return(await dm())[t]??null}async function dP(t,e){let n=await dm();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var nt=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function Ca(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function fP(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Ca(n.replace(/\n+/g," "),e)}function cS(t){let e=`*${nt(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${nt(t.repoLabel)} \xB7 \`${nt(t.branch||"?")}\` \xB7 lead *${nt(t.lead)}*`,r=t.kickoff?`> ${nt(Ca(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
|
|
459
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Qc(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function um(t,e){await Hr(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:cS(e)})}async function Jn(t,e,n,r=void 0){return om(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var aS=new Set;function zn(t){aS.add(t),t.catch(()=>{}).finally(()=>aS.delete(t))}var yt=t=>({response_type:"ephemeral",text:t}),pP="*/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._",mP=["claude","cursor","codex"];function im(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 cm=(t,e)=>yt(`No live Track matches \u201C${nt(e)}\u201D. Live: ${t.map(n=>`\`${nt(n.name)}\``).join(" \xB7 ")||"none"}`);async function lS(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 Gt(o,{...yt(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Gt(o,{...yt("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?rl.basename(c.base):"fresh workspace"),lead:"claude"},u=await om(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:cS(l),unfurl_links:!1});l.ts=u.ts,(await dm())[i.id]=l,await ve(),await Gt(o,{...yt(`\u2713 *${nt(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Gt(o,{...yt(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function hP(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await fm(n.id);s&&(s.archivedAt=Date.now(),await ve(),await um(e,s).catch(()=>{})),await Gt(r,{...yt(`\u2713 *${nt(n.name)}* archived.`),replace_original:!0})}catch(s){await Gt(r,{...yt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function wP(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Gt(o,{...yt(`\u2713 Handing *${nt(n.name)}* to ${nt(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Gt(o,{...yt(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function gP(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 Gt(r,{...yt(`\u2713 Stopped *${nt(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 Gt(r,{...yt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function yP(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Ca(`${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 *${nt(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function bP(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?rl.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 *${nt(s.name)}* \u2014 ${nt(i)}${a.branch?` \`${nt(a.branch)}\``:""} \xB7 lead ${nt(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
|
|
454
460
|
`)+(o?`
|
|
455
|
-
_${o} archived_`:"")}async function
|
|
456
|
-
\u2713 *${
|
|
457
|
-
\u2713 _answered_`}}]})}async function
|
|
458
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
459
|
-
`)){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 iN(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}var cN=new Set(["Write","Edit","NotebookEdit"]),lN=220,uN=160;function dN(t,e){let n=t?.tool_name??null,r={tool:n},o=t?.tool_input;if(!o||typeof o!="object")return r;if(cN.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=e?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,lN)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=nr(o.command.trim().replace(/\s+/g," ")).slice(0,uN));return r}function fN(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?qe(e.cwd,e.sessionId):e?.cwd?Yl(e.cwd):null}async function Lx(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await I(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":{let c=String(t.prompt??t.user_prompt??""),l=Zi(c);await i({type:"turn.user",actor:l?"peer":"user",payload:{promptHead:(l?l.text:c).slice(0,160)||null,...l?{peer:{socket:l.socket,name:l.name,mode:l.mode}}:{}}});break}case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:dN(t,o)});break;case"Stop":await pN(r,o,t,i);break;default:break}return a}async function pN(t,e,n,r){let o=[],s=fN(n,e);if(s)try{o=aN(await sN.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=iN(await L(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=d.model||"unknown",p=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=d.tokens[m];p.messageIds.push(d.messageId),u.set(f,p)}for(let[d,f]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:d,usd:In(d,f.tokens),tokens:f.tokens,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}Zo();ct();Bi();import wN from"node:fs";import qp from"node:fs/promises";import gN from"node:http";import Fo from"node:path";import Vc from"node:os";import{createRequire as yN}from"node:module";import Kp from"ws";import mN from"node:path";import hN from"node:os";var va=process.env.BOSTRAT_STATE_DIR||mN.join(process.env.HOME||hN.tmpdir(),".bostrat");function Wp(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 Fx(t=process.env){if(!t.BOSTRAT_PLANE_URL)return null;let e=Wp(t);return e==="127.0.0.1"||e==="::1"||e==="localhost"?null:{host:e}}function Ux(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 Kc=(()=>{try{return yN(import.meta.url)("../package.json").version||null}catch{return null}})(),bN=process.env.BOSTRAT_STATE_DIR||Fo.join(process.env.HOME||Vc.tmpdir(),".bostrat");function kN(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 xN({home:t=process.env.HOME||Vc.homedir(),env:e=process.env}={}){let n=r=>{try{return wN.statSync(r),!0}catch{return!1}};return{claude:n(Fo.join(t,".claude",".credentials.json")),codex:n(Fo.join(e.CODEX_HOME||Fo.join(t,".codex"),"auth.json"))}}var Nt=1,Hx=1e3,SN=3e4,vN=6e4,TN=1500,AN=500,Gc=100,EN=2e3,_N=25e3,IN=6e4,RN=6e4,$N=6e4;function Wx({url:t,token:e,nodeId:n=Vc.hostname(),name:r=Vc.hostname(),listTracks:o,detectAgents:s=null,sampleMetrics:a=null,metricsMs:i=RN,selfUrl:c=`http://127.0.0.1:${process.env.PORT||3362}`,log:l=console}){if(!t||!e)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");t=Ux(t);let u=c.replace(/^http/,"ws"),d=new Map,f=new Map,p=Fo.join(bN,"plane-link","cursor.json"),m={},w={},y=new Map,k=null,S=!1,$=!1,R=Hx,J=null,C=null,D=null,A=null,N=null,W=!1,at=0;async function it(){try{m=JSON.parse(await qp.readFile(p,"utf8"))}catch{m={}}}function E(){W=!0,!D&&(D=setTimeout(async()=>{if(D=null,!!W){W=!1;try{await qp.mkdir(Fo.dirname(p),{recursive:!0}),await qp.writeFile(p,JSON.stringify(m))}catch(K){l.error("[plane-link] cursor flush failed:",K?.message||K)}}},EN),D.unref?.())}function O(K){return!k||k.readyState!==Kp.OPEN?!1:(k.send(JSON.stringify(K)),!0)}function At(K){let b=Date.now();for(let{trackId:v,event:P}of K){let mt=y.get(P.id);y.set(P.id,{trackId:v,seq:P.seq,event:P,sentAt:b,tries:(mt?.tries??0)+1})}return O({v:Nt,ch:"events",type:"append",events:K})}function Gt(K){let b=null;for(let v of y.values())v.trackId===K&&(b==null||v.seq<b)&&(b=v.seq);return b}function gt(K){let b=w[K];if(b==null)return;let v=Gt(K),P=v==null?b:Math.min(b,v-1);P>(m[K]??0)&&(m[K]=P,E())}function g(){if(!S)return;let K=Date.now(),b=[...y.values()].filter(v=>v.event&&K-v.sentAt>=IN);if(b.length){b.sort((v,P)=>v.trackId===P.trackId?v.seq-P.seq:v.trackId<P.trackId?-1:1),l.log(`[plane-link] re-sending ${b.length} unacked event(s) the plane never confirmed`);for(let v=0;v<b.length;v+=Gc){let P=b.slice(v,v+Gc).map(mt=>({trackId:mt.trackId,event:mt.event}));if(!At(P))return}}}async function h(){if(!a)return;let K;try{K=await a()}catch(b){l.error("[plane-link] metrics sample failed:",b?.message||b);return}K&&O({v:Nt,ch:"ctl",type:"metrics",metrics:K})}function T(){a&&(N&&clearInterval(N),h(),N=setInterval(h,i),N.unref?.())}async function _(){let K;try{K=await o()}catch(b){l.error("[plane-link] backfill listTracks failed:",b?.message||b);return}O({v:Nt,ch:"ctl",type:"tracks",ids:K.map(b=>b.id).filter(b=>typeof b=="string")});for(let b of K){if(!S)return;let v;try{v=await L(b)}catch{continue}let P=m[b.id]??0,mt=v.filter(rt=>rt.seq>P&&typeof rt.id=="string");for(let rt=0;rt<mt.length;rt+=Gc){let Ot=mt.slice(rt,rt+Gc).map(oe=>({trackId:b.id,event:oe}));if(!At(Ot))return}}}function St(K){!S||!K?.trackId||typeof K.id!="string"||At([{trackId:K.trackId,event:K}])}let nt=new Map;async function dt(K){let{reqId:b,method:v,path:P,headers:mt={},body:rt}=K,Ot=Object.fromEntries(Object.entries(mt).filter(([,Te])=>Te!=null)),oe=new AbortController;nt.set(b,oe);try{if(K.host){await Jo(K,Ot,oe);return}let Te=await fetch(c+P,{method:v,headers:Ot,body:rt?Buffer.from(rt,"base64"):void 0,signal:oe.signal}),pn=Object.fromEntries(Te.headers);for(let se of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete pn[se];if(pn["content-type"]||(pn["content-type"]="application/octet-stream"),O({v:Nt,ch:"control",type:"http_head",reqId:b,status:Te.status,headers:pn}),Te.body){for await(let se of Te.body)if(!O({v:Nt,ch:"control",type:"http_data",reqId:b,data:Buffer.from(se).toString("base64")}))break}O({v:Nt,ch:"control",type:"http_end",reqId:b,error:null})}finally{nt.delete(b)}}function Jo(K,b,v){let{reqId:P,method:mt,path:rt,body:Ot}=K,oe=new URL(c);return new Promise(Te=>{let pn=gN.request({host:oe.hostname,port:oe.port,method:mt,path:rt,headers:{...b,host:K.host},signal:v.signal},se=>{let{connection:N1,"keep-alive":B1,"transfer-encoding":D1,upgrade:j1,...Nv}=se.headers;O({v:Nt,ch:"control",type:"http_head",reqId:P,status:se.statusCode||502,headers:Nv}),se.on("data",Bv=>{O({v:Nt,ch:"control",type:"http_data",reqId:P,data:Buffer.from(Bv).toString("base64")})||se.destroy()}),se.on("end",()=>{O({v:Nt,ch:"control",type:"http_end",reqId:P,error:null}),Te()}),se.on("error",()=>{O({v:Nt,ch:"control",type:"http_end",reqId:P,error:"view upstream error"}),Te()})});pn.on("error",se=>{O({v:Nt,ch:"control",type:"http_end",reqId:P,error:se?.message||"view request failed"}),Te()}),Ot&&pn.write(Buffer.from(Ot,"base64")),pn.end()})}function Bt(){if($)return;k=new Kp(t,{headers:{authorization:`Bearer ${e}`}}),k.on("open",async()=>{let b=null;try{b=await s?.()||null}catch{b=null}O({v:Nt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Kc,providers:xN(),...b?{agents:b}:{}})}),k.on("message",b=>{let v;try{v=JSON.parse(b)}catch{return}if(v.ch==="ctl"&&v.type==="hello_ok"){S=!0,R=Hx,J=null,l.log(`[plane-link] connected to plane as ${n}`),v.minNodeVersion&&Kc&&kN(Kc,v.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${Kc}; the plane asks for \u2265${v.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),A&&clearInterval(A),A=setInterval(()=>{O({v:Nt,ch:"ctl",type:"ping"}),g()},_N),A.unref?.(),T(),_().catch(P=>l.error("[plane-link] backfill failed:",P?.message||P));return}if(v.ch==="ctl"&&v.type==="usage_caps"){Ni(v.caps??{},{source:"plane",updatedAt:v.updatedAt??Date.now()}).then(P=>l.log(`[plane-link] spend caps ${Object.keys(P.caps).length?`updated (${Object.keys(P.caps).join(", ")})`:"cleared"} from plane`)).catch(P=>l.error("[plane-link] spend-caps push rejected:",P?.message||P));return}if(v.ch==="events"&&v.type==="ack"&&Array.isArray(v.ids)){let P=new Set;for(let mt of v.ids){let rt=y.get(mt);rt&&(y.delete(mt),(w[rt.trackId]??0)<rt.seq&&(w[rt.trackId]=rt.seq),P.add(rt.trackId))}for(let mt of P)gt(mt);return}if(v.ch==="control"&&v.type==="http_open"){dt(v).catch(P=>O({v:Nt,ch:"control",type:"http_end",reqId:v.reqId,error:P?.message||"tunnel error"}));return}if(v.ch==="control"&&v.type==="http_cancel"){nt.get(v.reqId)?.abort();return}if(v.ch==="attach"&&v.type==="open"){let P=new Kp(u+v.path,Array.isArray(v.protocols)?v.protocols:[],v.host?{headers:{host:v.host}}:void 0);d.set(v.streamId,P),f.set(v.streamId,[]),P.on("open",()=>{let rt=f.get(v.streamId)||[];f.delete(v.streamId);for(let{buf:Ot,bin:oe}of rt)P.send(Ot,{binary:oe})}),P.on("message",(rt,Ot)=>{let oe=Ot?rt:Buffer.from(String(rt));O({v:Nt,ch:"attach",type:"data",streamId:v.streamId,data:oe.toString("base64"),...Ot?{bin:!0}:{}})});let mt=()=>{f.delete(v.streamId),d.delete(v.streamId)&&O({v:Nt,ch:"attach",type:"close",streamId:v.streamId})};P.on("close",mt),P.on("error",mt);return}if(v.ch==="attach"&&v.type==="data"){let P=d.get(v.streamId);if(!P)return;let mt=Buffer.from(v.data,"base64"),rt=v.bin===!0,Ot=f.get(v.streamId);Ot?Ot.push({buf:mt,bin:rt}):P.send(mt,{binary:rt});return}if(v.ch==="attach"&&v.type==="close"){let P=d.get(v.streamId);d.delete(v.streamId),f.delete(v.streamId),P?.close();return}});let K=()=>{S&&l.log("[plane-link] disconnected from plane"),S=!1,A&&(clearInterval(A),A=null),N&&(clearInterval(N),N=null),y.clear(),w={};for(let P of d.values())P.close();d.clear(),f.clear();for(let P of nt.values())P.abort();if(nt.clear(),$||C)return;J==null&&(J=Date.now());let b=Date.now()-J<vN,v=b?TN+Math.random()*AN:R;C=setTimeout(()=>{C=null,Bt()},v),C.unref?.(),b||(R=Math.min(R*2,SN))};k.on("close",K),k.on("error",b=>{let v=/Unexpected server response: (\d+)/.exec(b?.message||"")?.[1];(v==="401"||v==="403")&&Date.now()-at>=$N&&(at=Date.now(),l.error(`[plane-link] plane refused this node's credentials (HTTP ${v}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),K()})}return Yt.on("event",St),it().then(Bt),{stop:()=>{$=!0,Yt.off("event",St),C&&clearTimeout(C),D&&clearTimeout(D),A&&clearInterval(A),N&&clearInterval(N),O({v:Nt,ch:"ctl",type:"bye",reason:"shutdown"}),k?.close()},status:()=>({connected:S,nodeId:n,cursor:{...m},inflight:y.size})}}import ON from"node:fs";import qx from"node:fs/promises";import Ta from"node:os";import CN from"node:path";hf();Ht();var MN=1024*1024,zc=t=>Math.round(t/MN);async function PN(t){try{let e=await qx.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:zc(r),diskTotalMb:zc(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function NN(){try{return(await he()).filter(e=>e.isBostrat).length}catch{return null}}async function BN(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await qx.readdir(CN.join(va,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function Kx({stateDir:t=va,now:e=Date.now}={}){let n=Ta.totalmem(),[r,o,s,a]=await Promise.all([fc().catch(()=>null),PN(ON.existsSync(t)?t:Ta.tmpdir()),NN(),BN()]),i=Ta.cpus()?.length||null,c=Ta.loadavg()?.[0]??null;return{ts:e(),loadavg1:c!=null?Math.round(c*100)/100:null,cpus:i,cpuPct:c!=null&&i?Math.min(100,Math.round(c/i*100)):null,memUsedMb:r!=null?zc(n-r):null,memTotalMb:zc(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Ta.uptime())}}ct();cu();Ya();ct();ur();function Gp(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 Gx=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Vx=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Jc=t=>Math.round(t*1e4)/1e4;async function zx(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=Gx(),d=0,f=[];for(let p of t||[]){let m=await L(p),w=Rn(m,{boxLabels:Gp(p)});if(w.turns===0){p.archivedAt?d++:f.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let y of w.unpricedModels||[])l.add(y);Vx(u,w.totalTokens);for(let[y,k]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:Gx()},n[y].usd=Jc(n[y].usd+k.usd),n[y].events+=k.events,Vx(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=Jc(r[y].usd+k.usd),r[y].events+=k.events;e.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return e.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Jc(o),asRecordedUsd:Jc(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:d+f.length,tracksSilentArchived:d,tracksSilentLive:f},tracks:e}}var Gn=t=>`$${t.toFixed(4)}`,Uo=(t,e)=>String(t).padEnd(e);function Jx(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 ${Gn(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),e.asRecordedUsd!=null&&Math.abs(e.asRecordedUsd-e.totalUsd)>=.005&&r.push(`as-recorded total ${Gn(e.asRecordedUsd)} \xB7 current-rates total ${Gn(e.totalUsd)} (${e.totalUsd>=e.asRecordedUsd?"+":""}${Gn(e.totalUsd-e.asRecordedUsd)})`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${Uo(a,26)} ${Uo(Gn(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(` ${Uo(a.name||a.id,30)} ${Gn(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Uo(c.label,40)} ${Uo(Gn(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${Uo(Gn(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
460
|
-
`)}Ud();Bi();ct();vd();import UN from"node:fs/promises";import HN from"node:path";iu();ur();var Yc=t=>t.payload?.ts??t.ts,DN=12e4,Vn=t=>Math.round(t*1e4)/1e4,jN=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function LN(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>Yc(S)>=e,a=ir([ys(n?.name),ys(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 $=ir(S.payload.task);$&&i.set(S.boxId,$)}let c=new Map,l=S=>{let $=S||"unattributed";return c.has($)||c.set($,{segments:[u(null)],lastTs:null,provider:null}),c.get($)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((S,$)=>Yc(S)-Yc($)),f=0,p=0,m=0,w={turns:0};for(let S of d){let $=l(S.boxId),R=$.segments[$.segments.length-1],J=Yc(S);switch($.lastTs!=null&&J>$.lastTs&&s(S)&&(R.activeMs+=Math.min(J-$.lastTs,DN)),$.lastTs=J,S.provider&&($.provider=S.provider),S.type){case"turn.user":{let C=S.payload?.text;if(S.source==="hook"||!String(C||"").trim())break;let D=u(ir(C));$.segments.push(D),s(S)&&(D.turns+=1);break}case"peer.message":{let C=S.payload?.text;if(!String(C||"").trim())break;let D=u(ir(C));$.segments.push(D),s(S)&&(D.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let C=S.payload?.tool;C&&(R.tools[C]=(R.tools[C]||0)+1),s(S)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let{usd:C,tokens:D,model:A,unpriced:N}=Ke(S);N&&(w.turns+=1),R.usd+=C,D&&(R.tokens.input+=D.input,R.tokens.output+=D.output,R.tokens.cacheRead+=D.cacheRead,R.tokens.cacheWrite+=D.cacheWrite),R.models[A]=(R.models[A]||0)+C,S.provider&&(R.provider=S.provider);break}case"handoff.done":s(S)&&(f+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,k=!1;for(let[S,$]of c)for(let R of $.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let C=R.textCat?.category??i.get(S)?.category??Kh(R.tools)?.category??a?.category??"uncategorized";y.has(C)||y.set(C,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let D=y.get(C);D.usd+=R.usd,D.activeMs+=R.activeMs,D.turns+=R.turns,D.toolCalls+=R.toolCalls;for(let[N,W]of Object.entries(R.models))D.models[N]=(D.models[N]||0)+W;let A=R.provider||$.provider;A&&(D.providers[A]=(D.providers[A]||0)+R.usd),A==="cursor"&&R.usd===0&&(k=!0)}return{perCategory:y,handoffs:f,merged:p,failed:m,sawCursorActivity:k,unpricedTurns:w.turns}}function Xx(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let w of t||[]){let{track:y,events:k}=w;if(!k?.length){a+=1;continue}let{perCategory:S,handoffs:$,merged:R,failed:J,sawCursorActivity:C,unpricedTurns:D}=LN(w,{since:r});C&&(i=!0),c+=D;let A={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:$,subtasksMerged:R,subtasksFailed:J};for(let[N,W]of S){A.usd+=W.usd,A.activeMs+=W.activeMs,A.turns+=W.turns,A.categories[N]={usd:Vn(W.usd),activeMs:W.activeMs};for(let[E,O]of Object.entries(W.models))A.models[E]=Vn((A.models[E]||0)+O);for(let[E,O]of Object.entries(W.providers))A.providers[E]=Vn((A.providers[E]||0)+O);o.has(N)||o.set(N,jN());let at=o.get(N);at.usd+=W.usd,at.activeMs+=W.activeMs,at.turns+=W.turns,at.toolCalls+=W.toolCalls;for(let[E,O]of Object.entries(W.models))at.models[E]=(at.models[E]||0)+O;for(let[E,O]of Object.entries(W.providers))at.providers[E]=(at.providers[E]||0)+O;let it=at.byTrack.get(y?.id)||{usd:0,activeMs:0};it.usd+=W.usd,it.activeMs+=W.activeMs,at.byTrack.set(y?.id,it)}(A.usd>0||A.activeMs>0||A.turns>0)&&(A.usd=Vn(A.usd),A.dominantCategory=FN(A.categories),s.push(A))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,p=new Map((t||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,y])=>({category:w,sharePct:f>0?Math.round((d==="usd"?y.usd:y.activeMs)/f*100):0,shareBasis:d,usd:Vn(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:Yx(y.models),providers:Yx(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((k,S)=>S[1].usd-k[1].usd||S[1].activeMs-k[1].activeMs).slice(0,2).map(([k,S])=>({trackId:k,name:p.get(k),usd:Vn(S.usd),activeMs:S.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:Vn(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var FN=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},Yx=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Vn(n)]));var Ho={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function WN(t){let e=String(t||"7d");return e in Ho?{key:e,ms:Ho[e]}:{key:"7d",ms:Ho["7d"]}}function qN(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=Li(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 KN(t){if(!t?.cwd)return null;try{let n=(await UN.readFile(HN.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 Zx(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}:WN(e),d=[];for(let f of t||[]){let p=await L(f),m=qN(f,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),d.push({track:f,events:m,brief:await KN(f)})}return{window:l,...Xx(d,{windowMs:u,now:c})}}ct();ur();var zn=36e5,nS=24*zn,GN=400,VN=12,Wo=t=>Math.round(t*1e4)/1e4;function zN({window:t,from:e,to:n,now:r=Date.now()}={}){let o=Number(e),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(t||"7d")in Ho?String(t||"7d"):"7d",i=Ho[a];return{key:a,since:i==null?null:r-i,until:r}}var JN=(t,e)=>t!=null&&e-t<=48*zn?zn:nS;function Qx(t,e){if(e===zn)return Math.floor(t/zn)*zn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function YN(t,e){if(e===zn)return t+zn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var tS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),eS=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},XN=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Wo(n)]));async function rS(t,e={}){let n=e.now??Date.now(),r=zN({...e,now:n}),o=r.since==null?nS:JN(r.since,r.until),s=A=>(r.since==null||A>=r.since)&&A<r.until,a=r.since!=null?r.until-r.since:null,i=A=>a!=null&&A>=r.since-a&&A<r.since,c={},l={},u=tS(),d=0,f=0,p=0,m=0,w=0,y=new Set,k=new Map,S=[],$=[],R=[],J=null;for(let A of t||[]){let W=(await L(A)).filter(E=>E.type==="cost.recorded");if(!W.length){A.archivedAt||R.push(A.name||A.id);continue}let at=[];for(let E of W){let O=E.ts??0;s(O)?at.push(E):i(O)&&(w+=Ke(E).usd)}if(!at.length)continue;let it=Rn(at,{boxLabels:Gp(A)});d+=it.totalUsd,f+=it.turns,p+=it.repricedTurns||0,m+=it.unpricedTurns||0;for(let E of it.unpricedModels||[])y.add(E);eS(u,it.totalTokens);for(let[E,O]of Object.entries(it.byModel))c[E]||={usd:0,events:0,tokens:tS()},c[E].usd=Wo(c[E].usd+O.usd),c[E].events+=O.events,eS(c[E].tokens,O.tokens);for(let[E,O]of Object.entries(it.byProvider))l[E]||={usd:0,events:0},l[E].usd=Wo(l[E].usd+O.usd),l[E].events+=O.events;for(let E of at){let{usd:O}=Ke(E);if(!O)continue;let At=Qx(E.ts??0,o),Gt=k.get(At)??{t:At,total:0,byProvider:{}};Gt.total+=O;let gt=E.provider||"unknown";Gt.byProvider[gt]=(Gt.byProvider[gt]||0)+O,k.set(At,Gt),(J==null||At<J)&&(J=At)}S.push({id:A.id,name:A.name||A.id,archived:!!A.archivedAt,usd:it.totalUsd,turns:it.turns,providers:Object.keys(it.byProvider)});for(let[E,O]of Object.entries(it.byBox))$.push({trackId:A.id,trackName:A.name||A.id,boxId:E,...O})}let C=r.since!=null?Qx(r.since,o):J,D=[];for(let A=C,N=0;A!=null&&A<r.until&&N<GN;A=YN(A,o),N++){let W=k.get(A);D.push(W?{t:A,total:Wo(W.total),byProvider:XN(W.byProvider)}:{t:A,total:0,byProvider:{}})}return S.sort((A,N)=>N.usd-A.usd),$.sort((A,N)=>N.usd-A.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:D,fleet:{totalUsd:Wo(d),turns:f,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:R},prior:a!=null?{totalUsd:Wo(w),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:$.slice(0,VN)}}Qt();import qo from"node:fs/promises";import ZN from"node:fs";import Lt from"node:path";import QN from"node:os";import{execFile as tB}from"node:child_process";import{promisify as eB}from"node:util";var nB=eB(tB),oS=process.env.BOSTRAT_STATE_DIR||Lt.join(process.env.HOME||QN.tmpdir(),".bostrat"),Xc=!1,Vp=null;async function zp(t,e){let n=await qo.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await qo.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await qo.mkdir(Lt.dirname(e),{recursive:!0}),await qo.copyFile(t,e),!0)}var rB=new Set([".claude",".codex",".cursor",".credentials.json","auth.json",".claude.json","api-token","node.env","certs","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function Zc(t,e,{skip:n=null}={}){let r=0,o=await qo.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(rB.has(s.name)||n?.has(s.name))continue;let a=Lt.join(t,s.name);s.isDirectory()?r+=await Zc(a,Lt.join(e,s.name)):s.isFile()&&(r+=await zp(a,Lt.join(e,s.name))?1:0)}return r}async function sS({stateDir:t=oS,dest:e=process.env.BOSTRAT_SYNC_DIR||`${oS}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await qo.mkdir(e,{recursive:!0});let o=0;o+=await zp(Lt.join(t,"tracks.json"),Lt.join(e,"tracks.json"))?1:0,o+=await zp(Lt.join(t,"events.jsonl"),Lt.join(e,"events.jsonl"))?1:0,o+=await Zc(Lt.join(t,"events"),Lt.join(e,"events")),o+=await Zc(Lt.join(t,"memory"),Lt.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Zc(Lt.join(u.cwd,".bostrat"),Lt.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>nB("git",["-C",e,...u]);ZN.existsSync(Lt.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 Al(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||Xc))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Xc&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Xc=!1,Vp=null}catch(u){Xc=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
461
|
-
`).slice(-3).join(" | ").slice(0,300);
|
|
462
|
-
`)
|
|
463
|
-
`).map(s=>s.replace(/\s+$/,"").
|
|
464
|
-
`).
|
|
465
|
-
|
|
466
|
-
`,{mode:384}),rl.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Ia(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 xS(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:bS.timingSafeEqual(n,r)}function SS(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 HB=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],WB=t=>HB.some(e=>e.test(t));function vS({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=SS(o);return xS(c,t)?a():WB(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||Ia(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 TS(t,e,{enforce:n=!0}={}){return!n||xS(SS(t),e)?!0:Ia(t)}We();sc();sc();import qB from"node:http";import KB from"node:net";function ol(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 sl(t){let e=ol(t.headers.host);return e?{slug:e,view:tf(e)}:null}var GB=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),VB=t=>{let e=GB(t.name);return`<!doctype html><meta charset="utf-8">
|
|
461
|
+
_${o} archived_`:"")}async function kP(t,e){let n=await Lr();if(!n)return yt("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 yt("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?(zn(lS(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),yt(`Starting *${nt(u)}* on ${nt(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:yP(u,i)}}case"archive":{if(!a.length)return yt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=im(await r.listTracks(),a.join(" "));return i?(zn(hP(t,n,{track:i,responseUrl:e.response_url})),yt(`Archiving *${nt(i.name)}*\u2026`)):cm(c,a.join(" "))}case"handoff":{let i=r.knownAgents??mP,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return yt(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=im(await r.listTracks(),l);return u?(zn(wP(t,n,{track:u,agent:c,responseUrl:e.response_url})),yt(`Handing *${nt(u.name)}* to ${nt(c)}\u2026`)):cm(f,l)}case"stop":{if(!a.length)return yt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=im(await r.listTracks(),a.join(" "));return i?(zn(gP(t,n,{track:i,responseUrl:e.response_url})),yt(`Stopping *${nt(i.name)}*\u2026`)):cm(c,a.join(" "))}case"list":return yt(await bP(r));default:return yt(pP)}}async function xP(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await fm(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 ve(),await Hr(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${nt(l||"question")}~
|
|
462
|
+
\u2713 *${nt(a)}*`}}]}).catch(()=>{})}}catch(c){await Gt(n.response_url,yt(`Couldn't answer: ${c?.message||c}`))}}async function SP(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;if(f?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:f}))return;f&&await i.sayToLead(c.id,vo(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 Gt(n.response_url,yt(`Couldn't act on the offer: ${c?.message||c}`))}}async function vP(t,e,n,r){let{trackId:o,offerId:s,choice:a}=r;try{let i=await t.deps.resolveTrack(o);if(!i)throw new Error("track not found");await t.deps.answerPrExists(i,{offerId:s,choice:a})}catch(i){await Gt(n.response_url,yt(`Couldn't act on the offer: ${i?.message||i}`))}}async function TP(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await Lr();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 Gt(e.response_url,{...yt(`Starting *${nt(s)}*\u2026`),replace_original:!0}),zn(lS(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"?zn(xP(t,r,e,o)):n.action_id==="bostrat-ship"?zn(SP(t,r,e,o)):n.action_id==="bostrat-pr-exists"&&zn(vP(t,r,e,o))}async function AP(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 dP(e.channel,e.thread_ts);if(!n)return;let r=await Lr();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await Jn(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function EP(t){return async e=>{if(e.type==="slash_commands")return kP(t,e.payload);if(e.type==="interactive"){await TP(t,e.payload);return}if(e.type==="events_api"){await AP(t,e.payload?.event);return}}}var _P=new Set(["turn.user","turn.agent","prompt.raised","prompt.answered","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","pr.state.changed","handoff.done","view.state.changed","mock.state.changed"]);function IP(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${nt(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:Ca(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 RP(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await Hr(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${nt(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Jn(t,e,r.question||"The lead needs an answer",IP(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await ve()}async function $P(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 ve(),await Hr(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${nt(s||"question")}~
|
|
463
|
+
\u2713 _answered_`}}]})}async function OP(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 Jn(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${nt(Ca(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await ve()}async function CP(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await ve();let s=r.action==="dismiss"?"dismissed":r.action==="pr-exists"?`PR${r.existingPr?.number?` #${r.existingPr.number}`:""} already open \u2014 pick how to ship`:r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await Hr(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function MP(t,e,n){let r=n.payload||{};if(!r.offerId||e.offers?.[r.offerId])return;let o=r.pr?.number?`#${r.pr.number}`:"a PR",s=(i,c,l)=>({type:"button",action_id:"bostrat-pr-exists",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,choice:i}),...l?{style:l}:{}}),a=await Jn(t,e,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${nt(r.branch)})`:""} is already open. Push the update to it, or open a new PR?`}},{type:"actions",elements:[s("push",`Push to ${o}`,"primary"),s("new","Open a new PR"),s("dismiss","Not now")]}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await ve()}async function NP(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await ve();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await Hr(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function PP(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??BP)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await rS(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${nt(o.name)}*${a}${o.group?` (group \`${nt(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${nt(a.name)}*`).join(", ");await Jn(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${nt(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function BP(t,e){let n=await nn(t);if(!n)return null;let r=Se(n,e);if(!r)return null;let o=await Oa.readFile(de(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function DP(t,e){if(!_P.has(e.type))return;let n=await fm(e.trackId);if(!n)return;let r=await Lr();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 ve(),await um(r,n);return;case"turn.agent":{if(!o.text)return;let s=fP(o.text);if(!s)return;await Jn(r,n,s,[{type:"section",text:{type:"mrkdwn",text:nt(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Qc(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return RP(r,n,e);case"prompt.answered":return $P(r,n,e);case"pr.offered":return OP(r,n,e);case"pr.offer.answered":return CP(r,n,e);case"pr.exists.offered":return MP(r,n,e);case"pr.exists.answered":return NP(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 Jn(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 ve(),await um(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Jn(r,n,`Live preview ${o.name}: ${tl(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${nt(o.name)}*: ${tl(o.slug)}
|
|
464
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return PP(r,n,e,t.deps)}}function uS(t){let e={deps:t};Xt.on("event",s=>{DP(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Lr()||(r&&clearInterval(r),r=null,n=oS({getToken:async()=>(await Lr())?.appToken,onEnvelope:EP(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}ge();ut();qe();Gd();Ps();import jP from"node:fs/promises";function LP(t){let e=new Map;for(let n of String(t||"").split(`
|
|
465
|
+
`)){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 FP(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}var UP=new Set(["Write","Edit","NotebookEdit"]),HP=220,WP=160;function qP(t,e){let n=t?.tool_name??null,r={tool:n},o=t?.tool_input;if(!o||typeof o!="object")return r;if(UP.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=e?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,HP)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=cr(o.command.trim().replace(/\s+/g," ")).slice(0,WP));return r}function KP(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Ke(e.cwd,e.sessionId):e?.cwd?su(e.cwd):null}async function dS(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await $(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":{let c=String(t.prompt??t.user_prompt??""),l=uc(c);await i({type:"turn.user",actor:l?"peer":"user",payload:{promptHead:(l?l.text:c).slice(0,160)||null,...l?{peer:{socket:l.socket,name:l.name,mode:l.mode}}:{}}});break}case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:qP(t,o)});break;case"Stop":await GP(r,o,t,i);break;default:break}return a}async function GP(t,e,n,r){let o=[],s=KP(n,e);if(s)try{o=LP(await jP.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=FP(await U(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:$n(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}})}is();ut();zi();import JP from"node:fs";import mm from"node:fs/promises";import YP from"node:http";import Yo from"node:path";import al from"node:os";import{createRequire as XP}from"node:module";import hm from"ws";import VP from"node:path";import zP from"node:os";var Ma=process.env.BOSTRAT_STATE_DIR||VP.join(process.env.HOME||zP.tmpdir(),".bostrat");function pm(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 fS(t=process.env){if(!t.BOSTRAT_PLANE_URL)return null;let e=pm(t);return e==="127.0.0.1"||e==="::1"||e==="localhost"?null:{host:e}}function pS(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 ol=(()=>{try{return XP(import.meta.url)("../package.json").version||null}catch{return null}})(),ZP=process.env.BOSTRAT_STATE_DIR||Yo.join(process.env.HOME||al.tmpdir(),".bostrat");function QP(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 tB({home:t=process.env.HOME||al.homedir(),env:e=process.env}={}){let n=r=>{try{return JP.statSync(r),!0}catch{return!1}};return{claude:n(Yo.join(t,".claude",".credentials.json")),codex:n(Yo.join(e.CODEX_HOME||Yo.join(t,".codex"),"auth.json"))}}var Bt=1,mS=1e3,eB=3e4,nB=6e4,rB=1500,oB=500,sl=100,sB=2e3,aB=25e3,iB=6e4,cB=6e4,lB=6e4;function hS({url:t,token:e,nodeId:n=al.hostname(),name:r=al.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=cB,selfUrl:l=`http://127.0.0.1:${process.env.PORT||3362}`,log:u=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=pS(t);let f=l.replace(/^http/,"ws"),d=new Map,p=new Map,m=Yo.join(ZP,"plane-link","cursor.json"),w={},y={},k=new Map,x=null,_=!1,O=!1,z=mS,C=null,P=null,T=null,D=null,j=null,it=!1,st=0;async function E(){try{w=JSON.parse(await mm.readFile(m,"utf8"))}catch{w={}}}function N(){it=!0,!T&&(T=setTimeout(async()=>{if(T=null,!!it){it=!1;try{await mm.mkdir(Yo.dirname(m),{recursive:!0}),await mm.writeFile(m,JSON.stringify(w))}catch(b){u.error("[plane-link] cursor flush failed:",b?.message||b)}}},sB),T.unref?.())}function Y(b){return!x||x.readyState!==hm.OPEN?!1:(x.send(JSON.stringify(b)),!0)}function $t(b){let I=Date.now();for(let{trackId:R,event:B}of b){let wt=k.get(B.id);k.set(B.id,{trackId:R,seq:B.seq,event:B,sentAt:I,tries:(wt?.tries??0)+1})}return Y({v:Bt,ch:"events",type:"append",events:b})}function dt(b){let I=null;for(let R of k.values())R.trackId===b&&(I==null||R.seq<I)&&(I=R.seq);return I}function g(b){let I=y[b];if(I==null)return;let R=dt(b),B=R==null?I:Math.min(I,R-1);B>(w[b]??0)&&(w[b]=B,N())}function h(){if(!_)return;let b=Date.now(),I=[...k.values()].filter(R=>R.event&&b-R.sentAt>=iB);if(I.length){I.sort((R,B)=>R.trackId===B.trackId?R.seq-B.seq:R.trackId<B.trackId?-1:1),u.log(`[plane-link] re-sending ${I.length} unacked event(s) the plane never confirmed`);for(let R=0;R<I.length;R+=sl){let B=I.slice(R,R+sl).map(wt=>({trackId:wt.trackId,event:wt.event}));if(!$t(B))return}}}async function v(){if(!i)return;let b;try{b=await i()}catch(I){u.error("[plane-link] metrics sample failed:",I?.message||I);return}b&&Y({v:Bt,ch:"ctl",type:"metrics",metrics:b})}function A(){i&&(j&&clearInterval(j),v(),j=setInterval(v,c),j.unref?.())}async function tt(){let b;try{b=await o()}catch(I){u.error("[plane-link] backfill listTracks failed:",I?.message||I);return}Y({v:Bt,ch:"ctl",type:"tracks",ids:b.filter(I=>!I.transferredTo).map(I=>I.id).filter(I=>typeof I=="string")});for(let I of b){if(!_)return;let R;try{R=await U(I)}catch{continue}let B=w[I.id]??0,wt=R.filter(ht=>ht.seq>B&&typeof ht.id=="string");for(let ht=0;ht<wt.length;ht+=sl){let Ut=wt.slice(ht,ht+sl).map(ie=>({trackId:I.id,event:ie}));if(!$t(Ut))return}}}function X(b){!_||!b?.trackId||typeof b.id!="string"||$t([{trackId:b.trackId,event:b}])}let et=new Map;async function rr(b){let{reqId:I,method:R,path:B,headers:wt={},body:ht}=b,Ut=Object.fromEntries(Object.entries(wt).filter(([,Ae])=>Ae!=null)),ie=new AbortController;et.set(I,ie);try{if(b.host){await Ot(b,Ut,ie);return}let Ae=await fetch(l+B,{method:R,headers:Ut,body:ht?Buffer.from(ht,"base64"):void 0,signal:ie.signal}),mn=Object.fromEntries(Ae.headers);for(let ce of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete mn[ce];if(mn["content-type"]||(mn["content-type"]="application/octet-stream"),Y({v:Bt,ch:"control",type:"http_head",reqId:I,status:Ae.status,headers:mn}),Ae.body){for await(let ce of Ae.body)if(!Y({v:Bt,ch:"control",type:"http_data",reqId:I,data:Buffer.from(ce).toString("base64")}))break}Y({v:Bt,ch:"control",type:"http_end",reqId:I,error:null})}finally{et.delete(I)}}function Ot(b,I,R){let{reqId:B,method:wt,path:ht,body:Ut}=b,ie=new URL(l);return new Promise(Ae=>{let mn=YP.request({host:ie.hostname,port:ie.port,method:wt,path:ht,headers:{...I,host:b.host},signal:R.signal},ce=>{let{connection:aj,"keep-alive":ij,"transfer-encoding":cj,upgrade:lj,...rT}=ce.headers;Y({v:Bt,ch:"control",type:"http_head",reqId:B,status:ce.statusCode||502,headers:rT}),ce.on("data",oT=>{Y({v:Bt,ch:"control",type:"http_data",reqId:B,data:Buffer.from(oT).toString("base64")})||ce.destroy()}),ce.on("end",()=>{Y({v:Bt,ch:"control",type:"http_end",reqId:B,error:null}),Ae()}),ce.on("error",()=>{Y({v:Bt,ch:"control",type:"http_end",reqId:B,error:"view upstream error"}),Ae()})});mn.on("error",ce=>{Y({v:Bt,ch:"control",type:"http_end",reqId:B,error:ce?.message||"view request failed"}),Ae()}),Ut&&mn.write(Buffer.from(Ut,"base64")),mn.end()})}function or(){if(O)return;x=new hm(t,{headers:{authorization:`Bearer ${e}`}}),x.on("open",async()=>{let I=null;try{I=await s?.()||null}catch{I=null}Y({v:Bt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:ol,providers:tB(),...I?{agents:I}:{}})}),x.on("message",I=>{let R;try{R=JSON.parse(I)}catch{return}if(R.ch==="ctl"&&R.type==="hello_ok"){_=!0,z=mS,C=null,u.log(`[plane-link] connected to plane as ${n}`),R.minNodeVersion&&ol&&QP(ol,R.minNodeVersion)&&u.error(`[plane-link] this node is bostrat-node ${ol}; the plane asks for \u2265${R.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),D&&clearInterval(D),D=setInterval(()=>{Y({v:Bt,ch:"ctl",type:"ping"}),h()},aB),D.unref?.(),A(),tt().catch(B=>u.error("[plane-link] backfill failed:",B?.message||B));return}if(R.ch==="ctl"&&R.type==="usage_caps"){Vi(R.caps??{},{source:"plane",updatedAt:R.updatedAt??Date.now()}).then(B=>u.log(`[plane-link] spend caps ${Object.keys(B.caps).length?`updated (${Object.keys(B.caps).join(", ")})`:"cleared"} from plane`)).catch(B=>u.error("[plane-link] spend-caps push rejected:",B?.message||B));return}if(R.ch==="ctl"&&R.type==="transferred"&&Array.isArray(R.tracks)){Promise.resolve().then(()=>a?.(R.tracks)).catch(B=>u.error("[plane-link] transfer notice failed:",B?.message||B));return}if(R.ch==="events"&&R.type==="ack"&&Array.isArray(R.ids)){let B=new Set;for(let wt of R.ids){let ht=k.get(wt);ht&&(k.delete(wt),(y[ht.trackId]??0)<ht.seq&&(y[ht.trackId]=ht.seq),B.add(ht.trackId))}for(let wt of B)g(wt);return}if(R.ch==="control"&&R.type==="http_open"){rr(R).catch(B=>Y({v:Bt,ch:"control",type:"http_end",reqId:R.reqId,error:B?.message||"tunnel error"}));return}if(R.ch==="control"&&R.type==="http_cancel"){et.get(R.reqId)?.abort();return}if(R.ch==="attach"&&R.type==="open"){let B=new hm(f+R.path,Array.isArray(R.protocols)?R.protocols:[],R.host?{headers:{host:R.host}}:void 0);d.set(R.streamId,B),p.set(R.streamId,[]),B.on("open",()=>{let ht=p.get(R.streamId)||[];p.delete(R.streamId);for(let{buf:Ut,bin:ie}of ht)B.send(Ut,{binary:ie})}),B.on("message",(ht,Ut)=>{let ie=Ut?ht:Buffer.from(String(ht));Y({v:Bt,ch:"attach",type:"data",streamId:R.streamId,data:ie.toString("base64"),...Ut?{bin:!0}:{}})});let wt=()=>{p.delete(R.streamId),d.delete(R.streamId)&&Y({v:Bt,ch:"attach",type:"close",streamId:R.streamId})};B.on("close",wt),B.on("error",wt);return}if(R.ch==="attach"&&R.type==="data"){let B=d.get(R.streamId);if(!B)return;let wt=Buffer.from(R.data,"base64"),ht=R.bin===!0,Ut=p.get(R.streamId);Ut?Ut.push({buf:wt,bin:ht}):B.send(wt,{binary:ht});return}if(R.ch==="attach"&&R.type==="close"){let B=d.get(R.streamId);d.delete(R.streamId),p.delete(R.streamId),B?.close();return}});let b=()=>{_&&u.log("[plane-link] disconnected from plane"),_=!1,D&&(clearInterval(D),D=null),j&&(clearInterval(j),j=null),k.clear(),y={};for(let B of d.values())B.close();d.clear(),p.clear();for(let B of et.values())B.abort();if(et.clear(),O||P)return;C==null&&(C=Date.now());let I=Date.now()-C<nB,R=I?rB+Math.random()*oB:z;P=setTimeout(()=>{P=null,or()},R),P.unref?.(),I||(z=Math.min(z*2,eB))};x.on("close",b),x.on("error",I=>{let R=/Unexpected server response: (\d+)/.exec(I?.message||"")?.[1];(R==="401"||R==="403")&&Date.now()-st>=lB&&(st=Date.now(),u.error(`[plane-link] plane refused this node's credentials (HTTP ${R}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),b()})}return Xt.on("event",X),E().then(or),{stop:()=>{O=!0,Xt.off("event",X),P&&clearTimeout(P),T&&clearTimeout(T),D&&clearInterval(D),j&&clearInterval(j),Y({v:Bt,ch:"ctl",type:"bye",reason:"shutdown"}),x?.close()},status:()=>({connected:_,nodeId:n,cursor:{...w},inflight:k.size})}}import uB from"node:fs";import wS from"node:fs/promises";import Na from"node:os";import dB from"node:path";_f();Wt();var fB=1024*1024,il=t=>Math.round(t/fB);async function pB(t){try{let e=await wS.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:il(r),diskTotalMb:il(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function mB(){try{return(await we()).filter(e=>e.isBostrat).length}catch{return null}}async function hB(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await wS.readdir(dB.join(Ma,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function gS({stateDir:t=Ma,now:e=Date.now}={}){let n=Na.totalmem(),[r,o,s,a]=await Promise.all([Tc().catch(()=>null),pB(uB.existsSync(t)?t:Na.tmpdir()),mB(),hB()]),i=Na.cpus()?.length||null,c=Na.loadavg()?.[0]??null;return{ts:e(),loadavg1:c!=null?Math.round(c*100)/100:null,cpus:i,cpuPct:c!=null&&i?Math.min(100,Math.round(c/i*100)):null,memUsedMb:r!=null?il(n-r):null,memTotalMb:il(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Na.uptime())}}ut();gu();ii();ut();hr();function wm(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 yS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),bS=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},cl=t=>Math.round(t*1e4)/1e4;async function kS(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=yS(),f=0,d=[];for(let p of t||[]){let m=await U(p),w=On(m,{boxLabels:wm(p)});if(w.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let y of w.unpricedModels||[])l.add(y);bS(u,w.totalTokens);for(let[y,k]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:yS()},n[y].usd=cl(n[y].usd+k.usd),n[y].events+=k.events,bS(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=cl(r[y].usd+k.usd),r[y].events+=k.events;e.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return e.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:cl(o),asRecordedUsd:cl(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:e}}var Yn=t=>`$${t.toFixed(4)}`,Xo=(t,e)=>String(t).padEnd(e);function xS(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 ${Yn(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),e.asRecordedUsd!=null&&Math.abs(e.asRecordedUsd-e.totalUsd)>=.005&&r.push(`as-recorded total ${Yn(e.asRecordedUsd)} \xB7 current-rates total ${Yn(e.totalUsd)} (${e.totalUsd>=e.asRecordedUsd?"+":""}${Yn(e.totalUsd-e.asRecordedUsd)})`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${Xo(a,26)} ${Xo(Yn(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(` ${Xo(a.name||a.id,30)} ${Yn(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Xo(c.label,40)} ${Xo(Yn(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))}${Xo(Yn(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
466
|
+
`)}Qd();zi();ut();Pd();import kB from"node:fs/promises";import xB from"node:path";wu();hr();var ll=t=>t.payload?.ts??t.ts,wB=12e4,Xn=t=>Math.round(t*1e4)/1e4,gB=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function yB(t,{since:e}){let{track:n,events:r,brief:o}=t,s=x=>ll(x)>=e,a=fr([_s(n?.name),_s(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let x of r)if(x.type==="subtask.delegated"&&x.boxId&&x.payload?.task){let _=fr(x.payload.task);_&&i.set(x.boxId,_)}let c=new Map,l=x=>{let _=x||"unattributed";return c.has(_)||c.set(_,{segments:[u(null)],lastTs:null,provider:null}),c.get(_)},u=x=>({textCat:x,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((x,_)=>ll(x)-ll(_)),d=0,p=0,m=0,w={turns:0};for(let x of f){let _=l(x.boxId),O=_.segments[_.segments.length-1],z=ll(x);switch(_.lastTs!=null&&z>_.lastTs&&s(x)&&(O.activeMs+=Math.min(z-_.lastTs,wB)),_.lastTs=z,x.provider&&(_.provider=x.provider),x.type){case"turn.user":{let C=x.payload?.text;if(x.source==="hook"||!String(C||"").trim())break;let P=u(fr(C));_.segments.push(P),s(x)&&(P.turns+=1);break}case"peer.message":{let C=x.payload?.text;if(!String(C||"").trim())break;let P=u(fr(C));_.segments.push(P),s(x)&&(P.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let C=x.payload?.tool;C&&(O.tools[C]=(O.tools[C]||0)+1),s(x)&&(O.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:C,tokens:P,model:T,unpriced:D}=Ge(x);D&&(w.turns+=1),O.usd+=C,P&&(O.tokens.input+=P.input,O.tokens.output+=P.output,O.tokens.cacheRead+=P.cacheRead,O.tokens.cacheWrite+=P.cacheWrite),O.models[T]=(O.models[T]||0)+C,x.provider&&(O.provider=x.provider);break}case"handoff.done":s(x)&&(d+=1);break;case"subtask.merged":s(x)&&(p+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,k=!1;for(let[x,_]of c)for(let O of _.segments){if(!(O.usd>0||O.activeMs>0||O.turns>0||O.toolCalls>0))continue;let C=O.textCat?.category??i.get(x)?.category??mw(O.tools)?.category??a?.category??"uncategorized";y.has(C)||y.set(C,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let P=y.get(C);P.usd+=O.usd,P.activeMs+=O.activeMs,P.turns+=O.turns,P.toolCalls+=O.toolCalls;for(let[D,j]of Object.entries(O.models))P.models[D]=(P.models[D]||0)+j;let T=O.provider||_.provider;T&&(P.providers[T]=(P.providers[T]||0)+O.usd),T==="cursor"&&O.usd===0&&(k=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:k,unpricedTurns:w.turns}}function vS(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let w of t||[]){let{track:y,events:k}=w;if(!k?.length){a+=1;continue}let{perCategory:x,handoffs:_,merged:O,failed:z,sawCursorActivity:C,unpricedTurns:P}=yB(w,{since:r});C&&(i=!0),c+=P;let T={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:_,subtasksMerged:O,subtasksFailed:z};for(let[D,j]of x){T.usd+=j.usd,T.activeMs+=j.activeMs,T.turns+=j.turns,T.categories[D]={usd:Xn(j.usd),activeMs:j.activeMs};for(let[E,N]of Object.entries(j.models))T.models[E]=Xn((T.models[E]||0)+N);for(let[E,N]of Object.entries(j.providers))T.providers[E]=Xn((T.providers[E]||0)+N);o.has(D)||o.set(D,gB());let it=o.get(D);it.usd+=j.usd,it.activeMs+=j.activeMs,it.turns+=j.turns,it.toolCalls+=j.toolCalls;for(let[E,N]of Object.entries(j.models))it.models[E]=(it.models[E]||0)+N;for(let[E,N]of Object.entries(j.providers))it.providers[E]=(it.providers[E]||0)+N;let st=it.byTrack.get(y?.id)||{usd:0,activeMs:0};st.usd+=j.usd,st.activeMs+=j.activeMs,it.byTrack.set(y?.id,st)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=Xn(T.usd),T.dominantCategory=bB(T.categories),s.push(T))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((t||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,y])=>({category:w,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:Xn(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:SS(y.models),providers:SS(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((k,x)=>x[1].usd-k[1].usd||x[1].activeMs-k[1].activeMs).slice(0,2).map(([k,x])=>({trackId:k,name:p.get(k),usd:Xn(x.usd),activeMs:x.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:Xn(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var bB=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},SS=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Xn(n)]));var Zo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function SB(t){let e=String(t||"7d");return e in Zo?{key:e,ms:Zo[e]}:{key:"7d",ms:Zo["7d"]}}function vB(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=Xi(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 TB(t){if(!t?.cwd)return null;try{let n=(await kB.readFile(xB.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 TS(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}:SB(e),f=[];for(let d of t||[]){let p=await U(d),m=vB(d,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:m,brief:await TB(d)})}return{window:l,...vS(f,{windowMs:u,now:c})}}ut();hr();var Zn=36e5,IS=24*Zn,AB=400,EB=12,Qo=t=>Math.round(t*1e4)/1e4;function _B({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 Zo?String(t||"7d"):"7d",i=Zo[a];return{key:a,since:i==null?null:r-i,until:r}}var IB=(t,e)=>t!=null&&e-t<=48*Zn?Zn:IS;function AS(t,e){if(e===Zn)return Math.floor(t/Zn)*Zn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function RB(t,e){if(e===Zn)return t+Zn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var ES=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),_S=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},$B=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Qo(n)]));async function RS(t,e={}){let n=e.now??Date.now(),r=_B({...e,now:n}),o=r.since==null?IS:IB(r.since,r.until),s=T=>(r.since==null||T>=r.since)&&T<r.until,a=r.since!=null?r.until-r.since:null,i=T=>a!=null&&T>=r.since-a&&T<r.since,c={},l={},u=ES(),f=0,d=0,p=0,m=0,w=0,y=new Set,k=new Map,x=[],_=[],O=[],z=null;for(let T of t||[]){let j=(await U(T)).filter(E=>E.type==="cost.recorded");if(!j.length){T.archivedAt||O.push(T.name||T.id);continue}let it=[];for(let E of j){let N=E.ts??0;s(N)?it.push(E):i(N)&&(w+=Ge(E).usd)}if(!it.length)continue;let st=On(it,{boxLabels:wm(T)});f+=st.totalUsd,d+=st.turns,p+=st.repricedTurns||0,m+=st.unpricedTurns||0;for(let E of st.unpricedModels||[])y.add(E);_S(u,st.totalTokens);for(let[E,N]of Object.entries(st.byModel))c[E]||={usd:0,events:0,tokens:ES()},c[E].usd=Qo(c[E].usd+N.usd),c[E].events+=N.events,_S(c[E].tokens,N.tokens);for(let[E,N]of Object.entries(st.byProvider))l[E]||={usd:0,events:0},l[E].usd=Qo(l[E].usd+N.usd),l[E].events+=N.events;for(let E of it){let{usd:N}=Ge(E);if(!N)continue;let Y=AS(E.ts??0,o),$t=k.get(Y)??{t:Y,total:0,byProvider:{}};$t.total+=N;let dt=E.provider||"unknown";$t.byProvider[dt]=($t.byProvider[dt]||0)+N,k.set(Y,$t),(z==null||Y<z)&&(z=Y)}x.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:st.totalUsd,turns:st.turns,providers:Object.keys(st.byProvider)});for(let[E,N]of Object.entries(st.byBox))_.push({trackId:T.id,trackName:T.name||T.id,boxId:E,...N})}let C=r.since!=null?AS(r.since,o):z,P=[];for(let T=C,D=0;T!=null&&T<r.until&&D<AB;T=RB(T,o),D++){let j=k.get(T);P.push(j?{t:T,total:Qo(j.total),byProvider:$B(j.byProvider)}:{t:T,total:0,byProvider:{}})}return x.sort((T,D)=>D.usd-T.usd),_.sort((T,D)=>D.usd-T.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:P,fleet:{totalUsd:Qo(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:O},prior:a!=null?{totalUsd:Qo(w),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:_.slice(0,EB)}}zt();import ts from"node:fs/promises";import OB from"node:fs";import Lt from"node:path";import CB from"node:os";import{execFile as MB}from"node:child_process";import{promisify as NB}from"node:util";var PB=NB(MB),$S=process.env.BOSTRAT_STATE_DIR||Lt.join(process.env.HOME||CB.tmpdir(),".bostrat"),ul=!1,gm=null;async function ym(t,e){let n=await ts.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await ts.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ts.mkdir(Lt.dirname(e),{recursive:!0}),await ts.copyFile(t,e),!0)}var BB=new Set([".claude",".codex",".cursor",".credentials.json","auth.json",".claude.json","api-token","node.env","certs","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function dl(t,e,{skip:n=null}={}){let r=0,o=await ts.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(BB.has(s.name)||n?.has(s.name))continue;let a=Lt.join(t,s.name);s.isDirectory()?r+=await dl(a,Lt.join(e,s.name)):s.isFile()&&(r+=await ym(a,Lt.join(e,s.name))?1:0)}return r}async function OS({stateDir:t=$S,dest:e=process.env.BOSTRAT_SYNC_DIR||`${$S}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ts.mkdir(e,{recursive:!0});let o=0;o+=await ym(Lt.join(t,"tracks.json"),Lt.join(e,"tracks.json"))?1:0,o+=await ym(Lt.join(t,"events.jsonl"),Lt.join(e,"events.jsonl"))?1:0,o+=await dl(Lt.join(t,"events"),Lt.join(e,"events")),o+=await dl(Lt.join(t,"memory"),Lt.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await dl(Lt.join(u.cwd,".bostrat"),Lt.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>PB("git",["-C",e,...u]);OB.existsSync(Lt.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 Dl(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||ul))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,ul&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),ul=!1,gm=null}catch(u){ul=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
467
|
+
`).slice(-3).join(" | ").slice(0,300);f!==gm&&(gm=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)}}}Gf();ge();co();Wt();cs();import{execFile as DB}from"node:child_process";import{promisify as jB}from"node:util";import{randomUUID as LB}from"node:crypto";import FB from"node:fs";import UB from"node:os";import HB from"node:path";var tr=jB(DB),fl=t=>new Promise(e=>setTimeout(e,t)),er=Ql,bm=process.env.BOSTRAT_USAGE_PROBE_CWD||HB.join(UB.homedir(),".bostrat","usage-probe"),WB=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,qB=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,KB=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,CS=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Qn=null,Pa=null,Ba=null;async function GB(){try{return await tr(L,F("has-session","-t",er)),!0}catch{return!1}}function VB(){Qn&&clearTimeout(Qn),Qn=setTimeout(()=>zB().catch(()=>{}),WB),Qn.unref&&Qn.unref()}async function zB(){Qn&&(clearTimeout(Qn),Qn=null);try{await tr(L,F("kill-session","-t",er))}catch{}}async function JB(){let t=Date.now()+KB;for(;;){let e=await km().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await fl(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await fl(500)}}async function YB(){if(await GB())return;if(!await Sn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");FB.mkdirSync(bm,{recursive:!0}),await hn(bm);let t=LB();await tr(L,F("new-session","-d","-s",er,"-c",bm,"-x","200","-y","60",...Zl()));let e=`${Xl} --session-id ${t}`;await tr(L,F("send-keys","-t",er,e,"Enter")),await JB()}async function km(){let{stdout:t}=await tr(L,F("capture-pane","-t",er,"-p"));return t}async function XB(){await YB(),await tr(L,F("send-keys","-t",er,"-l","/usage")),await tr(L,F("send-keys","-t",er,"Enter"));let t=Date.now()+qB,e="";for(;await fl(400),e=await km(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await fl(900),e=await km(),await tr(L,F("send-keys","-t",er,"Escape")).catch(()=>{}),VB(),e}function ZB(t){let e=String(t||"").split(`
|
|
468
|
+
`).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 QB(t){let e=String(t||"").split(`
|
|
469
|
+
`).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 MS({fresh:t=!1}={}){return!t&&Pa&&Date.now()-Pa.at<CS?Pa.data:Ba||(Ba=(async()=>{try{let e=await XB(),n=ZB(e),r={...n,contributors:QB(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Pa={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 Pa={at:Date.now()-(CS-3e3),data:n},n}finally{Ba=null}})(),Ba)}var tD=262144;function eD(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`}var nD=new Set([300]),rD=new Set([10080]);function oD(t){let e=Number(t);return!Number.isFinite(e)||e<=0?null:nD.has(e)?"session":rD.has(e)?"week":"other"}function sD(t){let e=Number(t);return!Number.isFinite(e)||e<=0?"Window":e%1440===0?`Window \xB7 ${e/1440}d`:e%60===0?`Window \xB7 ${e/60}h`:`Window \xB7 ${e}m`}function aD(t,e,n){if(!t||typeof t!="object")return null;let r=Number(t.used_percent);if(!Number.isFinite(r))return null;let o=null,s=Number(t.resets_at),a=Number(t.resets_in_seconds);return Number.isFinite(s)&&s>0?o=s*1e3:Number.isFinite(a)&&a>=0&&(o=(Number.isFinite(e)?e:n)+a*1e3),{pct:Math.round(r),resets:o!=null?eD((o-n)/1e3):null,resetsAt:o}}var Da=t=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:t});function iD(t,e=Date.now()){let n=String(t||"").split(`
|
|
470
|
+
`),r=!1;for(let o=n.length-1;o>=0;o--){let s=n[o].trim();if(!s||s[0]!=="{"||!s.includes("token_count"))continue;let a;try{a=JSON.parse(s)}catch{continue}let i=a?.payload;if(i?.type!=="token_count")continue;r=!0;let c=i.rate_limits;if(!c)return Da(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Da(r);[c.primary,c.secondary].forEach((p,m)=>{let w=aD(p,l,e);if(!w)return;let y=oD(p.window_minutes);y===null&&(y=m===0?"session":"week"),y==="session"&&!u.session?u.session=w:y==="week"&&!u.week?u.week=w:(u.other??=[]).push({label:sD(p.window_minutes),...w})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let d=c.credits;return d&&(d.has_credits||d.unlimited)&&(u.credits={balance:String(d.balance??""),unlimited:!!d.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...Da(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return Da(r)}return Da(r)}async function xm(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await pr(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 Yt(n.file,tD),o=iD(r);return{...e,session:o.session,week:o.week,other:o.other,planType:o.planType,credits:o.credits,limitReached:o.limitReached,subscription:!!(o.session||o.week||o.other?.length),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 NS(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 pl({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 xm(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await xm(null))}return n.has("cursor")&&r.push(await pl({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function pl(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?xm(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await MS({fresh:e})}}sr();zt();zt();function cD(t=process.env){return!!(bt(t)&&t.BOSTRAT_PLANE_TOKEN)}var lD=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),ml=(t,e)=>Object.assign(new Error(t),{status:e});async function PS(t,e){if(!cD(e))throw ml("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${bt(e)}${t}`,{headers:lD(e)})}catch(o){throw ml(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw ml(r?.error||`plane replied ${n.status}`,n.status);return r}async function BS(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await PS(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function DS(t,{env:e=process.env}={}){if(!t)throw ml("message id required",400);return(await PS(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}Fs();kc();function Sm(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 uD(t){let[e,n]=t.split("/"),r=Sm(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 jS(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(uD).filter(Boolean)}function dD(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function LS(t){let e=jS(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:dD,allowed(r){if(n)return!0;let o=Sm((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function FS(t,e){let n=jS(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Sm(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import US from"node:crypto";import hl from"node:fs";import fD from"node:path";function HS(t=process.env,{stateDir:e=Ma,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=fD.join(e,"api-token");try{let a=hl.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=US.randomBytes(32).toString("base64url");try{hl.mkdirSync(e,{recursive:!0}),hl.writeFileSync(o,s+`
|
|
471
|
+
`,{mode:384}),hl.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function ja(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 WS(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:US.timingSafeEqual(n,r)}function qS(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 pD=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],mD=t=>pD.some(e=>e.test(t));function KS({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=qS(o);return WS(c,t)?a():mD(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||ja(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 GS(t,e,{enforce:n=!0}={}){return!n||WS(qS(t),e)?!0:ja(t)}qe();gc();gc();import hD from"node:http";import wD from"node:net";function wl(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 gl(t){let e=wl(t.headers.host);return e?{slug:e,view:pf(e)}:null}var gD=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),yD=t=>{let e=gD(t.name);return`<!doctype html><meta charset="utf-8">
|
|
467
472
|
<meta http-equiv="refresh" content="3">
|
|
468
473
|
<title>${e} \u2014 starting</title>
|
|
469
474
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
470
475
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
471
|
-
<p><b>${e}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
476
|
+
<p><b>${e}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function VS(t,e,n){let r=gl(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(yD(o));if(o.state!=="live"&&o.state!=="unhealthy")return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...t.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":t.headers.host,"x-forwarded-proto":t.headers["x-forwarded-proto"]||"http"};delete a.authorization,delete a.cookie;let i=hD.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 zS(t,e,n){let r=gl(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
|
|
472
477
|
\r
|
|
473
478
|
bostrat: no live view "${s}"\r
|
|
474
|
-
`),e.destroy(),!0;let a=
|
|
479
|
+
`),e.destroy(),!0;let a=wD.connect(o.port,"127.0.0.1",()=>{let c=[`${t.method} ${t.url} HTTP/1.1`];for(let[l,u]of Object.entries(t.headers))l!=="host"&&(l==="authorization"||l==="cookie"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
475
480
|
`)+`\r
|
|
476
481
|
\r
|
|
477
|
-
`),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
|
|
478
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,JB)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${YB}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=QB.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);if(i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`,i.length>MS)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${MS}-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>CS)throw new Error(`${r.length} bullets \u2014 over the ${CS}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function NS(t,{repos:e=tm()}={}){let n=new Set(e);return(await kr()).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 BS(t,{md:e,day:n=null}){let r=tD(e),o=n==null||n===""?ZB():String(n);if(!XB.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await I({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}zs();Bn();ct();zs();Qt();xr();Bn();Cs();cc();import{execFile as eD}from"node:child_process";import{promisify as nD}from"node:util";var nm=nD(eD),DS=3e4,rD=30*6e4,oD=DS,sD=10*6e4,aD=10080*6e4,iD=60*6e4,cD=8,lD=1440*6e4,uD=3,jS="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",em=60*6e4,dD=15*6e4,al=new Map,il=new Map,fD=/could not resolve|not found|no pull requests?/i,pD=t=>fD.test(String(t?.message||t||""));function mD(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<em)}var LS={fast:oD,slow:sD,decay:iD};function FS(t,e){return t.armedAt&&e-t.armedAt<rD||mD(t,e)?"fast":e-(t.firstSeen||0)>aD?"decay":"slow"}function hD(t,e){return LS[FS(t,e)]}function Ca(t,e,n,r){let o=t.lanes?.get(V(e.repo,e.number))||{};return ic({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function wD(t,e){ze("watch");try{let{stdout:n}=await nm("gh",["pr","view",String(e),"--repo",t,"--json",jS],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Ae({slug:t})}});return JSON.parse(n||"null")}catch(n){throw Cn(n)&&Mn(n),n}}async function gD(t){ze("watch");try{let{stdout:e}=await nm("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",jS],{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 Cn(e)&&Mn(e),e}}async function yD(){let t=new Map;for(let e of await et().catch(()=>[]))t.set(e.id,e);return t}async function bD(t,e){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function kD(t,e){let n=await vr(t);return n?xo(n,e):null}async function xD(t,e,n){let r=await vr(t);if(!r)throw new Error(`no local checkout for ${t}`);return So(r,e,{method:n})}async function SD(t,e){let n=await vr(t);if(!n)throw new Error(`no local checkout for ${t}`);return Vs(n,e)}async function vD(t,e){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function Ko(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 TD=10*6e4;async function AD(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>lD){await Ko(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 Ko(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Ko(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<TD)return;await Ko(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 Ko(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 Ko(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await US(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Ra(t,e,n){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function ED(t,e){try{return await nm("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function $a(t,e,n,r=()=>!0){return(await L(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var Oa=(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 _D(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 Ki(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Vi(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<dD;await ab(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await sb(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await $a(a,"pushed",t.number,d=>d.payload?.sha===s)&&await Ra(a,"pushed",{...Oa(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>em)await Gi(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 Gi(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Ca(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await $a(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await Ra(a,"ci-fixed",{...Oa(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Gi(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>em)await zi(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await zi(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Ca(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await $a(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Ra(a,"reconciled",{...Oa(t,e),base:i.baseRefName||null}).catch(()=>{}),await zi(t.repo,t.number).catch(()=>{})}}}}async function ID(t,e,n,r){let o=Ca(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await $a(s,"closed",t.number)&&await Ra(s,"closed",{...Oa(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 RD(t,e,n){Ca(n,t,"lost",null);let r=e.get(t.trackId);r&&!await $a(r,"lost",t.number)&&await Ra(r,"lost",Oa(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function US(t,e,n,r,o={}){let s=Ca(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await L(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(t.number))||await r.emitMerged(a,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(t.repo,t.number).catch(()=>{}),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function $D(t={}){let{now:e=Date.now(),auth:n=sf,view:r=wD,list:o=gD,links:s=cb,trackMap:a=yD,emitMerged:i=bD,mark:c=Nn,archiveIfArmed:l=Je,notifyIfArmed:u=Ye,notifyIfResolved:d=Ic,batchThreshold:f=uD,detail:p=kD,merge:m=xD,updateBranch:w=SD,lookup:y=Wt,disarm:k=Bs,emitDisarmed:S=vD,localHasCommit:$=ED}=t;if(!(await n().catch(()=>null))?.authenticated||Wi()>0)return 0;let C=(await s().catch(()=>[])).filter(E=>e-(al.get(V(E.repo,E.number))||0)>=hD(E,e));if(!C.length)return 0;let D=Date.now(),A=new Map,N=new Map;for(let E of C){let O=FS(E,e);A.set(V(E.repo,E.number),{lane:O,intervalMs:LS[O]}),al.set(V(E.repo,E.number),e),N.has(E.repo)||N.set(E.repo,[]),N.get(E.repo).push(E)}let W=await a().catch(()=>new Map),at={now:e,lanes:A,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,detail:p,merge:m,updateBranch:w,lookup:y,disarm:k,emitDisarmed:S,localHasCommit:$};for(let[E,O]of N){let At,Gt=new Set;if(O.length>f)At=await o(E).catch(()=>null);else{At=new Map;for(let gt of O)try{let g=await r(E,gt.number);g?.number!=null&&At.set(Number(g.number),g)}catch(g){pD(g)&&Gt.add(V(gt.repo,gt.number))}}if(At)for(let gt of O){let g=V(gt.repo,gt.number),h=At.get(Number(gt.number));if(!h){if(Gt.has(g)){let T=(il.get(g)||0)+1;il.set(g,T),T>=cD&&(await RD(gt,W,at),il.delete(g),al.delete(g))}continue}il.delete(g),h.state==="MERGED"||h.state==="CLOSED"?(h.state==="MERGED"?await US(gt,h,W,at):await ID(gt,h,W,at),al.delete(g)):(await _D(gt,h,W,at).catch(()=>{}),gt.autoMerge&&await AD(gt,W,at).catch(()=>{}))}}let it={};for(let{lane:E}of A.values())it[E]=(it[E]||0)+1;return console.log(`[pr-watch] probed ${C.length} link(s) (${Object.entries(it).map(([E,O])=>`${E} ${O}`).join(", ")}) in ${Date.now()-D}ms`),C.length}function HS(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await $D()}catch{}finally{t=!1}}},DS)}Ht();ct();xr();import{randomUUID as OD}from"node:crypto";var CD=2e4,WS=3,Go=new Map,rm=new Set;async function qS(t,e,n){let r=await n(t).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===e?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function MD(t={}){let{sessions:e=null,tracks:n=null,append:r=I,read:o=L,now:s=Date.now()}=t;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=e??await he(),i=n??await et()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(u.archivedAt||!u.leadBox)continue;let d=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let f=c.get(d.name);if(f)if(l.add(d.name),f.alternateOn){let p=(Go.get(d.name)||0)+1;Go.set(d.name,p),p===WS&&(rm.add(d.name),await qS(u,d.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:OD(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(Go.get(d.name)||0)>=WS;if(Go.delete(d.name),p||!rm.has(d.name)){rm.add(d.name);let m=await qS(u,d.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...Go.keys()])l.has(u)||Go.delete(u)}var Ma=null,om=!1;function KS(){return Ma?()=>{}:(Ma=setInterval(()=>{om||(om=!0,MD().catch(()=>{}).finally(()=>{om=!1}))},CD),Ma.unref?.(),()=>{clearInterval(Ma),Ma=null})}ho();pf();ct();zs();ho();import{randomUUID as PD}from"node:crypto";async function ND(){let t=await Promise.resolve().then(()=>(xr(),J0));return{sayToLead:t.sayToLead,queueShipCompleteNotify:t.queueShipCompleteNotify}}var sm={listRepoPrs:Sr,readEvents:L,appendEvent:I,sayDeps:ND};async function BD(t,e=sm){let n=t?.workspace?.branch;if(!n||!t?.cwd)return null;let r;try{r=await e.listRepoPrs(t.cwd,{caller:"pr-exists"})}catch{return null}let o=(r||[]).find(s=>s.headRefName===n);return o?{number:o.number,url:o.url??null,title:o.title??null}:null}async function am(t,e,{actions:n,notify:r=!1}={},o=sm){let s=e?.payload||{};if(!Array.isArray(n)||!n.includes("pr")||s.toPr||Array.isArray(s.repos)&&s.repos.length||!s.branch)return null;let a=await BD(t,o);if(!a)return null;let i={id:t.id,cwd:t.cwd};await o.appendEvent(i,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.boxId,payload:{ts:Date.now(),offerId:s.offerId,action:"pr-exists",actions:n,...r?{notify:!0}:{},existingPr:{number:a.number}}});let c=PD();return await o.appendEvent(i,{type:"pr.exists.offered",actor:"system",source:"orchestrator",boxId:e.boxId,payload:{ts:Date.now(),offerId:c,srcOfferId:s.offerId??null,branch:s.branch,base:s.base??null,pr:a,actions:n,...r?{notify:!0}:{},offeredRebase:Array.isArray(s.offered)&&s.offered.includes("rebase")}}),{offerId:c,pr:a}}async function im(t,{offerId:e,choice:n}={},r=sm){if(!e)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(t),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===e);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===e))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=Fi.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(t.id,mo(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(t.id,Fy(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:t.id,cwd:t.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:e,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(t.id,{actions:l})}return{say:c,choice:n}}import fn from"node:fs/promises";import Vo from"node:path";import ZS from"node:os";import GS from"node:fs/promises";import cl from"node:path";import{execFile as DD}from"node:child_process";import{promisify as jD}from"node:util";import{fileURLToPath as LD}from"node:url";var FD=jD(DD),zS=cl.dirname(LD(import.meta.url)),UD=cl.resolve(zS,"..","..","client","dist"),HD="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",WD=new Map([...HD].map((t,e)=>[t,e]));function qD(t){let e=[],n=0,r=0;for(let o of t){let s=WD.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 KD(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=qD(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 GD(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 VS=new Map;async function VD(t){let e;try{e=await GS.stat(t)}catch{return null}let n=VS.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await GS.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:KD(r.mappings||"")};return VS.set(t,o),o}catch{return null}}var cm=null;function zD(){return cm||(cm=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):FD("git",["rev-parse","--short","HEAD"],{cwd:cl.resolve(zS,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),cm}var JD=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function YD(t,{distDir:e=UD}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(JD)){let[a,i,c,l]=s,u=await VD(cl.join(e,"assets",`${i}.map`)),d=u?GD(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function JS(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await zD();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await YD(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var XD=()=>process.env.BOSTRAT_STATE_DIR||Vo.join(process.env.HOME||ZS.tmpdir(),".bostrat"),Zn=()=>Vo.join(XD(),"telemetry"),QS=()=>Vo.join(Zn(),".relay-offsets.json"),ZD=50,QD=14,t1=3e4,YS=500,re=(t,e)=>typeof t=="string"?t.slice(0,e):"";function e1(t,e){if(!t||typeof t!="object")return null;let n=re(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:re(t.anonId,64),sessionId:re(t.sessionId,64),userId:re(t.userId,64),release:re(t.release,64),appVersion:re(t.appVersion,32),route:re(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:re(t.error.kind,256),message:re(t.error.message,8192),stack:re(t.error.stack,32768),fingerprint:re(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:re(s?.n,64),r:re(s?.r,128)}))),o}var tv=t=>new Date(t).toISOString().slice(0,10),XS=0;async function ev(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,ZD):[]).map(s=>e1(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await JS(s);await fn.mkdir(Zn(),{recursive:!0});let o=Vo.join(Zn(),`${tv(e)}.ndjson`);return await fn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
479
|
-
`).join(""),"utf8"),e-
|
|
480
|
-
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=YS){let p=d.slice(f,f+YS).map(w=>r1(w)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await ov(r)}return{sent:o,disabled:!1}}var lm=null,um=!1;function sv(){lm||(lm=setInterval(async()=>{if(!um){um=!0;try{await o1()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{um=!1}}},t1),lm.unref?.())}import dl from"node:fs/promises";import uv from"node:path";import s1 from"node:os";var dv=()=>process.env.BOSTRAT_STATE_DIR||uv.join(process.env.HOME||s1.tmpdir(),".bostrat"),fv=()=>uv.join(dv(),"dictation-glossary.json"),a1=100,i1=500,c1=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],fl=null,dm=null,fm=Promise.resolve();function av(){let t={},e=Date.now();for(let n of c1)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function pv(){return fl||(dm??=dl.readFile(fv(),"utf8").then(t=>JSON.parse(t)?.terms??av()).catch(()=>av()).then(t=>fl=t).finally(()=>dm=null),dm)}async function iv(){let t=JSON.stringify({terms:fl},null,2),e=fv(),n=`${e}.${process.pid}.tmp`;await dl.mkdir(dv(),{recursive:!0}),await dl.writeFile(n,t),await dl.rename(n,e)}function l1(){return fm=fm.then(iv,iv),fm.catch(()=>{})}async function pm(){return Object.values(await pv()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var u1=t=>Math.max(1,Math.ceil(t.length/4));async function d1(){let t=[],e=0;for(let{term:n}of await pm()){let r=u1(n);if(!(e+r>i1)&&(t.push(n),e+=r,t.length>=a1))break}return t}async function f1(t,e){let n=await pv(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var cv=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),ul=t=>t.toLowerCase();function lv(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function mv(t,e){let n=cv(t),r=cv(e);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=ul(n[u])===ul(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)ul(n[c])===ul(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(lv(r[l])&&i.add(r[l]),l++);for(;l<s;l++)lv(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await f1(u,"learned");return await l1(),[...i]}var Pa=null;function p1(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 hv(){if(!Pa)return null;let t=await wa();if(!t||t.apiKey.slice(-6)!==Pa.keyTail)return null;let{keyTail:e,...n}=Pa;return n}async function wv(t,e){let n=await wa();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 d1())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=p1(o.status,i);c.persistent&&(Pa={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 Pa=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ct();var mm={"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 m1(t){if(!t)return null;let e=String(t).trim();if(mm[e])return e;let n=null;for(let r of Object.keys(mm))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function h1(t,e={}){let n=mm[m1(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 w1(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 Na=null;async function g1(){if(!Na)return null;let t=await Lc();if(!t||t.apiKey.slice(-6)!==Na.keyTail)return null;let{keyTail:e,...n}=Na;return n}async function y1(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=w1(e.status,o);s.persistent&&(Na={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}Na=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 b1={openai:y1};async function gv(){let t=await Lc();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await g1()}:{configured:!1}}async function yv(){let t=await Lc();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 b1[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function bv(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=h1(e,s),i=await I(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var Av=ut.dirname(x1(import.meta.url)),kv=Number(process.env.PORT)||3362,pl=Wp(process.env),Ev=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,zo=gS(process.env.BOSTRAT_ALLOW_IPS),bm=yS(process.env.BOSTRAT_VIEW_FRONT_CIDRS,sl),_v=kS(),Iv=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=ve();x.disable("x-powered-by");x.use((t,e,n)=>{e.setHeader("x-content-type-options","nosniff"),ol(t.headers.host)||(e.setHeader("x-frame-options","DENY"),e.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});x.use((t,e,n)=>{if(Ia(t)||zo.allowed(zo.clientIp(t))||bm.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(AS);x.use(vS({token:_v,enforce:Iv}));x.use("/api/mocks",ve.json({limit:"1mb"}));x.use("/api/mocks",ve.raw({type:"image/png",limit:"24mb"}));x.use("/api/mocks",ve.raw({type:["video/mp4","video/webm"],limit:"105mb"}));x.use("/api/runbooks",ve.json({limit:"1mb"}));x.use("/api/telemetry",ve.json({limit:"256kb"}));x.use("/api/hook",ve.json({limit:"2mb"}));x.use("/api/dictation",ve.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",ve.raw({type:()=>!0,limit:"25mb"}));x.use(ve.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:te,hostTmuxAlive:await xn(),autoTriggerPct:Ev})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await he()})});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 or(n,r||void 0);e.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;e.status(a).json({error:s})}});x.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!bt(n))return e.status(400).json({error:"invalid session name"});if(!await tt(n))return e.status(404).json({error:"session not found"});try{await _e(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});x.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!bt(n))return e.status(400).json({error:"invalid session name"});let r=await tt(n);if(!r)return e.status(404).json({error:"session not found"});let o=await ps(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});x.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!bt(n))return e.status(400).json({error:"invalid session name"});try{let r=await ng(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=Ai(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 xv=10;x.get("/api/idle-guard",async(t,e)=>{try{e.json(Y0({tracks:await et(),views:await Qd(),queuedOf:n=>Co(n)}))}catch{e.status(500).json({busy:!0,reasons:["error"]})}});x.get("/api/tracks",async(t,e)=>{let n=await et(),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>xv){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,xv).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:ua,agentAvailability:await xc(),tickets:await gx()})});x.get("/api/cost",async(t,e)=>{try{let n=await et();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await zx(n);if(r.cursorUsage=Fd(),t.query.format==="text")return e.type("text/plain").send(Jx(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 Zx(await et(),{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 rS(await et(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=Fd(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=$s(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json($s())});x.post("/api/usage/caps",async(t,e)=>{try{e.json(await Ni(t.body?.caps??{},{source:"local"}))}catch(n){e.status(400).json({error:n?.message||String(n)})}});x.get("/api/usage/limits",async(t,e)=>{try{let n=t.query.fresh==="1"||t.query.fresh==="true";e.json(await fS(await et(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/hook",async(t,e)=>{try{let{mode:n,target:r}=await vm(t.get(Fa),{byToken:Wf,legacy:()=>Ls(t.body?.session_id,t.body?.cwd)});if(n==="refused")return Xo("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Xo("legacy-denied",{reason:"tokenless-denied",hook:t.body?.hook_event_name}),e.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&t.body?.session_id&&r.box?.sessionId&&t.body.session_id!==r.box.sessionId&&Xo("mismatch",{track:r.track.id,box:r.box.name}),Xo(n,{track:r?.track?.id,hook:t.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await Lx(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(!nv(zo.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await ev(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 Ue(),r=new Set((await Promise.all(n.map(a=>st(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await rs()).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 Ga(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:Ka(),calls:Zy(),latency:ek()})});x.post("/api/github/refresh",(t,e)=>{Mm(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await mS(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 hS(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function km(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 ml({repo:t=null,number:e=null,branch:n=null}){let r=t?await st(t):null;if(r&&e!=null){let s=await Wt(r,e),a=s&&await z(s.trackId);if(a)return a}return n?(await et()).find(s=>s.workspace?.branch===n&&km(s,t,r))??null:null}async function Rv({repo:t=null,number:e=null,branch:n=null}){let r=await ml({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await st(t).catch(()=>null):null;return(await et()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&km(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 ml({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await st(s).catch(()=>null):null);await I({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function A1(t){let e=(await et()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await st(n.path);if(r)for(let o of n.prs){if(await Wt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&km(a,n.path,r));s&&await ne({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 ck();await A1(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=t.body||{};try{if(s){let w=o||"merge";if(!Gs.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let k=await st((await tn(n)).path);if(!k)throw new Error("repo has no GitHub remote");let S=await ml({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 Vs(n,y),await ne({repo:k,number:y,trackId:S.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Ps(k,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await br(k,y,i===!0),await Ns(k,y,c===!0&&i!==!0),Pe(),await Se("auto-armed",{number:y,url:d,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:y,auto:!0})}let f=await So(n,r,{method:o||"squash"});await Se("merged",{number:f.number,url:d,title:u,branch:l,repo:n});let p=await st(n).catch(()=>null),m=!1;if(p&&(i===!0&&await br(p,f.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Ns(p,f.number,!0).catch(()=>{}),await Ye(p,f.number).catch(()=>{}),m=await Je(p,f.number).catch(()=>!1),await Nn(p,f.number,"merged").catch(()=>{})),i===!0&&!m){let w=await ml({repo:n,number:f.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Rr(w.id).catch(()=>{})}e.json(f)}catch(f){let p=f?.message||String(f),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;e.status(m).json({error:p})}});x.post("/api/git/prs/disarm-auto",async(t,e)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=t.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await st((await tn(n)).path).catch(()=>null),l=c?await Bs(c,i).catch(()=>null):null;try{await lf(n,i)}catch(u){if(!l)throw u}Pe(),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 st(n).catch(()=>null),s=await fk(n,{method:r||"merge",onMerged:a=>{Se("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Ye(o,a.number).catch(()=>{}).then(()=>Je(o,a.number)).catch(()=>{}).then(()=>Nn(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:dk()})});x.post("/api/git/prs/merge-track",async(t,e)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=t.body||{};try{let i=a||"merge";if(!Gs.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return e.status(400).json({error:"trackId required"});let c=await z(n);if(!c)return e.status(404).json({error:`Track ${n} isn't on this node`});let l=(await kr()).filter(f=>f.trackId===c.id&&f.state!=="merged"&&f.state!=="closed");if(!l.length)return e.status(409).json({error:"no open PRs on this Track"});for(let f of l)await br(f.repo,f.number,o===!0).catch(()=>{}),await Ns(f.repo,f.number,s===!0&&o!==!0).catch(()=>{});let u=[],d=[];for(let f of l){let p={repo:f.repo,number:f.number,ok:!0,action:"armed"};u.push(p);try{let m=await vr(f.repo);if(!m)throw new Error(`no local checkout for ${f.repo}`);let w=await xo(m,f.number);d.push({r:p,l:f,repoPath:m,pr:w,plan:lk(w,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:f,l:p,repoPath:m,pr:w,plan:y}of uk(d))try{if(y.action==="skip")f.action="skipped",f.reason=y.reason;else if(y.action==="merge")await So(m,p.number,{method:i}),await Se("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await Ye(p.repo,p.number).catch(()=>{}),await Je(p.repo,p.number).catch(()=>{}),await Nn(p.repo,p.number,"merged").catch(()=>{}),f.action="merged";else{if(y.action==="update-arm"&&(await Vs(m,p.number).catch(()=>{}),f.action="updated-armed"),!await Ps(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:w.url,title:w.title,branch:w.headRefName,repo:m,track:c,...f.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){f.ok=!1,f.action="failed",f.reason=k?.message||String(k)}if(Pe(),!u.some(f=>f.ok)){if(o===!0)for(let f of l)await br(f.repo,f.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let f of l)await Ns(f.repo,f.number,!1).catch(()=>{});return e.status(502).json({error:u.map(f=>`${f.repo}#${f.number}: ${f.reason}`).join("; "),results:u})}e.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/disarm-track",async(t,e)=>{let{trackId:n}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let r=await z(n);if(!r)return e.status(404).json({error:`Track ${n} isn't on this node`});let o=(await kr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Bs(a.repo,a.number).catch(()=>null),c=await vr(a.repo).catch(()=>null);c&&await lf(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}))}Pe(),e.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let i=await z(n);if(!i)return e.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=pe(i.workspace),d,f=Array.isArray(a)?a.filter(Boolean):[];if(f.length)d=f.map(w=>{let y=u.find(k=>k.name===w||k.repo===w||k.base===w||k.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(d=await go(i),!d.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of d)try{let y=await df(w.dir,{title:r,body:o,base:s}),k=w.repo||await st(w.dir).catch(()=>null);k&&y.number&&await ne({repo:k,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:k??w.name}}),p.push({repo:w.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(w=>w.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await df(i.cwd,{title:r,body:o,base:s}),l=await st(i.cwd).catch(()=>null);l&&c.number&&await ne({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 z(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await go(n),o=new Map(r.map(a=>[a.base,a])),s=pe(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 Zf(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 Qf(t.params.id,t.params.name);e.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;e.status(o).json({error:r})}});x.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await xo(n,r),s=await st(n).catch(()=>null),a=await Rv({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=pe(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await pk(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ut.join("ci",i.name):"ci",d=ut.join(a.cwd,".bostrat",u),f=ut.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ft.promises.mkdir(d,{recursive:!0}),await Ft.promises.writeFile(ut.join(a.cwd,f),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
482
|
+
`),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 JS(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}.${bD(n.host)}/`}function bD(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 wl(e)&&pf(n[0])?n.slice(1).join("."):e}No();Pf();Rc();zt();var YS=1e4,XS=(...t)=>fetch(...t);function ZS(t=process.env){return!!(bt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function QS({loopId:t,outcome:e,note:n=null,env:r=process.env}={}){if(!ZS(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),YS);try{let a=await XS(`${bt(r)}/v1/loops/${encodeURIComponent(t)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:e,...n?{note:n}:{}}),signal:o.signal}),i=await a.json().catch(()=>({}));return a.ok?{ok:!0,source:"plane",status:a.status,run:i?.run||null}:{ok:!1,source:"plane",status:a.status,error:i?.error||`plane ${a.status}`}}catch(a){return{ok:!1,source:"unreachable",error:a?.message||String(a)}}finally{clearTimeout(s)}}async function vm({env:t=process.env}={}){if(!ZS(t))return{loops:[],source:"unconfigured"};let e=new AbortController,n=setTimeout(()=>e.abort(),YS);try{let r=await XS(`${bt(t)}/v1/loops`,{headers:{authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},signal:e.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}kn();ut();jn();Rc();var tv=10,ev=200,kD=120,xD=24,SD=/^\d{4}-\d{2}-\d{2}$/;function Tm(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var vD=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function nv(t,e=Date.now()){if(!t)return e-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(t).trim());if(n)return e-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(t);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${t}" \u2014 use 24h, 7d, or an ISO date`)}var TD=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function AD(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=aa(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(`
|
|
483
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,kD)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${xD}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=TD.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);if(i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`,i.length>ev)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${ev}-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>tv)throw new Error(`${r.length} bullets \u2014 over the ${tv}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function rv(t,{repos:e=Tm()}={}){let n=new Set(e);return(await _r()).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 ov(t,{md:e,day:n=null}){let r=AD(e),o=n==null||n===""?vD():String(n);if(!SD.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await $({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}na();jn();ut();na();zt();Ir();jn();Fs();kc();import{execFile as ED}from"node:child_process";import{promisify as _D}from"node:util";var Em=_D(ED),sv=3e4,ID=30*6e4,RD=sv,$D=10*6e4,OD=10080*6e4,CD=60*6e4,MD=8,ND=1440*6e4,PD=3,av="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Am=60*6e4,BD=15*6e4,yl=new Map,bl=new Map,DD=/could not resolve|not found|no pull requests?/i,jD=t=>DD.test(String(t?.message||t||""));function LD(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Am)}var iv={fast:RD,slow:$D,decay:CD};function cv(t,e){return t.armedAt&&e-t.armedAt<ID||LD(t,e)?"fast":e-(t.firstSeen||0)>OD?"decay":"slow"}function FD(t,e){return iv[cv(t,e)]}function Ha(t,e,n,r){let o=t.lanes?.get(V(e.repo,e.number))||{};return bc({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function UD(t,e){Je("watch");try{let{stdout:n}=await Em("gh",["pr","view",String(e),"--repo",t,"--json",av],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Ee({slug:t})}});return JSON.parse(n||"null")}catch(n){throw Nn(n)&&Pn(n),n}}async function HD(t){Je("watch");try{let{stdout:e}=await Em("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",av],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Ee({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw Nn(e)&&Pn(e),e}}async function WD(){let t=new Map;for(let e of await at().catch(()=>[]))t.set(e.id,e);return t}async function qD(t,e){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function KD(t,e){let n=await $r(t);return n?$o(n,e):null}async function GD(t,e,n){let r=await $r(t);if(!r)throw new Error(`no local checkout for ${t}`);return Oo(r,e,{method:n})}async function VD(t,e){let n=await $r(t);if(!n)throw new Error(`no local checkout for ${t}`);return ea(n,e)}async function zD(t,e){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function es(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 JD=10*6e4;async function YD(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>ND){await es(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 es(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await es(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<JD)return;await es(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 es(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 es(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await lv(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function La(t,e,n){await $({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function XD(t,e){try{return await Em("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Fa(t,e,n,r=()=>!0){return(await U(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var Ua=(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 ZD(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 rc(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await sc(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<BD;await Ab(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await Tb(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Fa(a,"pushed",t.number,f=>f.payload?.sha===s)&&await La(a,"pushed",{...Ua(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Am)await oc(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 oc(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Ha(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Fa(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await La(a,"ci-fixed",{...Ua(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await oc(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Am)await ac(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await ac(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Ha(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Fa(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await La(a,"reconciled",{...Ua(t,e),base:i.baseRefName||null}).catch(()=>{}),await ac(t.repo,t.number).catch(()=>{})}}}}async function QD(t,e,n,r){let o=Ha(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Fa(s,"closed",t.number)&&await La(s,"closed",{...Ua(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 t1(t,e,n){Ha(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Fa(r,"lost",t.number)&&await La(r,"lost",Ua(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function lv(t,e,n,r,o={}){let s=Ha(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await U(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 e1(t={}){let{now:e=Date.now(),auth:n=yf,view:r=UD,list:o=HD,links:s=_b,trackMap:a=WD,emitMerged:i=qD,mark:c=Dn,archiveIfArmed:l=Ye,notifyIfArmed:u=Xe,notifyIfResolved:f=Lc,batchThreshold:d=PD,detail:p=KD,merge:m=GD,updateBranch:w=VD,lookup:y=qt,disarm:k=qs,emitDisarmed:x=zD,localHasCommit:_=XD}=t;if(!(await n().catch(()=>null))?.authenticated||ec()>0)return 0;let C=(await s().catch(()=>[])).filter(E=>e-(yl.get(V(E.repo,E.number))||0)>=FD(E,e));if(!C.length)return 0;let P=Date.now(),T=new Map,D=new Map;for(let E of C){let N=cv(E,e);T.set(V(E.repo,E.number),{lane:N,intervalMs:iv[N]}),yl.set(V(E.repo,E.number),e),D.has(E.repo)||D.set(E.repo,[]),D.get(E.repo).push(E)}let j=await a().catch(()=>new Map),it={now:e,lanes:T,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:w,lookup:y,disarm:k,emitDisarmed:x,localHasCommit:_};for(let[E,N]of D){let Y,$t=new Set;if(N.length>d)Y=await o(E).catch(()=>null);else{Y=new Map;for(let dt of N)try{let g=await r(E,dt.number);g?.number!=null&&Y.set(Number(g.number),g)}catch(g){jD(g)&&$t.add(V(dt.repo,dt.number))}}if(Y)for(let dt of N){let g=V(dt.repo,dt.number),h=Y.get(Number(dt.number));if(!h){if($t.has(g)){let v=(bl.get(g)||0)+1;bl.set(g,v),v>=MD&&(await t1(dt,j,it),bl.delete(g),yl.delete(g))}continue}bl.delete(g),h.state==="MERGED"||h.state==="CLOSED"?(h.state==="MERGED"?await lv(dt,h,j,it):await QD(dt,h,j,it),yl.delete(g)):(await ZD(dt,h,j,it).catch(()=>{}),dt.autoMerge&&await YD(dt,j,it).catch(()=>{}))}}let st={};for(let{lane:E}of T.values())st[E]=(st[E]||0)+1;return console.log(`[pr-watch] probed ${C.length} link(s) (${Object.entries(st).map(([E,N])=>`${E} ${N}`).join(", ")}) in ${Date.now()-P}ms`),C.length}function uv(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await e1()}catch{}finally{t=!1}}},sv)}Wt();ut();Ir();import{randomUUID as n1}from"node:crypto";var r1=2e4,dv=3,ns=new Map,_m=new Set;async function fv(t,e,n){let r=await n(t).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===e?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function o1(t={}){let{sessions:e=null,tracks:n=null,append:r=$,read:o=U,now:s=Date.now()}=t;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=e??await we(),i=n??await at()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(u.archivedAt||u.transferredTo||!u.leadBox)continue;let f=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!f||f.agent!=="claude"||f.state!=="live")continue;let d=c.get(f.name);if(d)if(l.add(f.name),d.alternateOn){let p=(ns.get(f.name)||0)+1;ns.set(f.name,p),p===dv&&(_m.add(f.name),await fv(u,f.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:n1(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(ns.get(f.name)||0)>=dv;if(ns.delete(f.name),p||!_m.has(f.name)){_m.add(f.name);let m=await fv(u,f.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ns.keys()])l.has(u)||ns.delete(u)}var Wa=null,Im=!1;function pv(){return Wa?()=>{}:(Wa=setInterval(()=>{Im||(Im=!0,o1().catch(()=>{}).finally(()=>{Im=!1}))},r1),Wa.unref?.(),()=>{clearInterval(Wa),Wa=null})}To();Af();ut();na();To();import{randomUUID as s1}from"node:crypto";async function a1(){let t=await Promise.resolve().then(()=>(Ir(),xx));return{sayToLead:t.sayToLead,queueShipCompleteNotify:t.queueShipCompleteNotify}}var Rm={listRepoPrs:Rr,readEvents:U,appendEvent:$,sayDeps:a1};async function i1(t,e=Rm){let n=t?.workspace?.branch;if(!n||!t?.cwd)return null;let r;try{r=await e.listRepoPrs(t.cwd,{caller:"pr-exists"})}catch{return null}let o=(r||[]).find(s=>s.headRefName===n);return o?{number:o.number,url:o.url??null,title:o.title??null}:null}async function $m(t,e,{actions:n,notify:r=!1}={},o=Rm){let s=e?.payload||{};if(!Array.isArray(n)||!n.includes("pr")||s.toPr||Array.isArray(s.repos)&&s.repos.length||!s.branch)return null;let a=await i1(t,o);if(!a)return null;let i={id:t.id,cwd:t.cwd};await o.appendEvent(i,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.boxId,payload:{ts:Date.now(),offerId:s.offerId,action:"pr-exists",actions:n,...r?{notify:!0}:{},existingPr:{number:a.number}}});let c=s1();return await o.appendEvent(i,{type:"pr.exists.offered",actor:"system",source:"orchestrator",boxId:e.boxId,payload:{ts:Date.now(),offerId:c,srcOfferId:s.offerId??null,branch:s.branch,base:s.base??null,pr:a,actions:n,...r?{notify:!0}:{},offeredRebase:Array.isArray(s.offered)&&s.offered.includes("rebase")}}),{offerId:c,pr:a}}async function Om(t,{offerId:e,choice:n}={},r=Rm){if(!e)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(t),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===e);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===e))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=Zi.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(t.id,vo(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(t.id,sb(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:t.id,cwd:t.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:e,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(t.id,{actions:l})}return{say:c,choice:n}}import pn from"node:fs/promises";import rs from"node:path";import kv from"node:os";import mv from"node:fs/promises";import kl from"node:path";import{execFile as c1}from"node:child_process";import{promisify as l1}from"node:util";import{fileURLToPath as u1}from"node:url";var d1=l1(c1),wv=kl.dirname(u1(import.meta.url)),f1=kl.resolve(wv,"..","..","client","dist"),p1="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",m1=new Map([...p1].map((t,e)=>[t,e]));function h1(t){let e=[],n=0,r=0;for(let o of t){let s=m1.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 w1(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=h1(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 g1(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 hv=new Map;async function y1(t){let e;try{e=await mv.stat(t)}catch{return null}let n=hv.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await mv.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:w1(r.mappings||"")};return hv.set(t,o),o}catch{return null}}var Cm=null;function b1(){return Cm||(Cm=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):d1("git",["rev-parse","--short","HEAD"],{cwd:kl.resolve(wv,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Cm}var k1=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function x1(t,{distDir:e=f1}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(k1)){let[a,i,c,l]=s,u=await y1(kl.join(e,"assets",`${i}.map`)),f=u?g1(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 gv(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await b1();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await x1(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var S1=()=>process.env.BOSTRAT_STATE_DIR||rs.join(process.env.HOME||kv.tmpdir(),".bostrat"),nr=()=>rs.join(S1(),"telemetry"),xv=()=>rs.join(nr(),".relay-offsets.json"),v1=50,T1=14,A1=3e4,yv=500,ae=(t,e)=>typeof t=="string"?t.slice(0,e):"";function E1(t,e){if(!t||typeof t!="object")return null;let n=ae(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:ae(t.anonId,64),sessionId:ae(t.sessionId,64),userId:ae(t.userId,64),release:ae(t.release,64),appVersion:ae(t.appVersion,32),route:ae(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:ae(t.error.kind,256),message:ae(t.error.message,8192),stack:ae(t.error.stack,32768),fingerprint:ae(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:ae(s?.n,64),r:ae(s?.r,128)}))),o}var Sv=t=>new Date(t).toISOString().slice(0,10),bv=0;async function vv(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,v1):[]).map(s=>E1(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await gv(s);await pn.mkdir(nr(),{recursive:!0});let o=rs.join(nr(),`${Sv(e)}.ndjson`);return await pn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
484
|
+
`).join(""),"utf8"),e-bv>6*3600*1e3&&(bv=e,_1(e).catch(()=>{})),r.length}async function _1(t=Date.now()){let e=Sv(t-T1*24*3600*1e3),n=[];try{n=await pn.readdir(nr())}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 pn.rm(rs.join(nr(),s),{force:!0});let o=await Av();for(let s of r)delete o[s];await Ev(o)}var xl=new Map;function Tv(t,e=Date.now()){let n=t||"unknown",r=xl.get(n);return r||(xl.size>512&&xl.clear(),r={tokens:40,at:e},xl.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 Av(){try{return JSON.parse(await pn.readFile(xv(),"utf8"))||{}}catch{return{}}}async function Ev(t){await pn.mkdir(nr(),{recursive:!0}),await pn.writeFile(xv(),JSON.stringify(t),"utf8")}function I1(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||kv.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 R1({fetchImpl:t=fetch}={}){let e=await Rx();if(!e)return{sent:0,disabled:!0};let n;try{n=(await pn.readdir(nr())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await Av(),o=0;for(let s of n){let a=rs.join(nr(),s),i=r[s]||0,c;try{let d=await pn.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(`
|
|
485
|
+
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=yv){let p=f.slice(d,d+yv).map(w=>I1(w)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await Ev(r)}return{sent:o,disabled:!1}}var Mm=null,Nm=!1;function _v(){Mm||(Mm=setInterval(async()=>{if(!Nm){Nm=!0;try{await R1()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Nm=!1}}},A1),Mm.unref?.())}import vl from"node:fs/promises";import Cv from"node:path";import $1 from"node:os";var Mv=()=>process.env.BOSTRAT_STATE_DIR||Cv.join(process.env.HOME||$1.tmpdir(),".bostrat"),Nv=()=>Cv.join(Mv(),"dictation-glossary.json"),O1=100,C1=500,M1=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Tl=null,Pm=null,Bm=Promise.resolve();function Iv(){let t={},e=Date.now();for(let n of M1)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function Pv(){return Tl||(Pm??=vl.readFile(Nv(),"utf8").then(t=>JSON.parse(t)?.terms??Iv()).catch(()=>Iv()).then(t=>Tl=t).finally(()=>Pm=null),Pm)}async function Rv(){let t=JSON.stringify({terms:Tl},null,2),e=Nv(),n=`${e}.${process.pid}.tmp`;await vl.mkdir(Mv(),{recursive:!0}),await vl.writeFile(n,t),await vl.rename(n,e)}function N1(){return Bm=Bm.then(Rv,Rv),Bm.catch(()=>{})}async function Dm(){return Object.values(await Pv()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var P1=t=>Math.max(1,Math.ceil(t.length/4));async function B1(){let t=[],e=0;for(let{term:n}of await Dm()){let r=P1(n);if(!(e+r>C1)&&(t.push(n),e+=r,t.length>=O1))break}return t}async function D1(t,e){let n=await Pv(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var $v=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Sl=t=>t.toLowerCase();function Ov(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Bv(t,e){let n=$v(t),r=$v(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]=Sl(n[u])===Sl(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;)Sl(n[c])===Sl(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(Ov(r[l])&&i.add(r[l]),l++);for(;l<s;l++)Ov(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await D1(u,"learned");return await N1(),[...i]}var qa=null;function j1(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 Dv(){if(!qa)return null;let t=await Ea();if(!t||t.apiKey.slice(-6)!==qa.keyTail)return null;let{keyTail:e,...n}=qa;return n}async function jv(t,e){let n=await Ea();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 B1())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=j1(o.status,i);c.persistent&&(qa={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 qa=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ut();var jm={"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 L1(t){if(!t)return null;let e=String(t).trim();if(jm[e])return e;let n=null;for(let r of Object.keys(jm))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function F1(t,e={}){let n=jm[L1(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 U1(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 Ka=null;async function H1(){if(!Ka)return null;let t=await Zc();if(!t||t.apiKey.slice(-6)!==Ka.keyTail)return null;let{keyTail:e,...n}=Ka;return n}async function W1(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=U1(e.status,o);s.persistent&&(Ka={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}Ka=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 q1={openai:W1};async function Lv(){let t=await Zc();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await H1()}:{configured:!1}}async function Fv(){let t=await Zc();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 q1[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function Uv(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=F1(e,s),i=await $(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var Vv=mt.dirname(G1(import.meta.url)),Hv=Number(process.env.PORT)||3362,Al=pm(process.env),zv=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,os=LS(process.env.BOSTRAT_ALLOW_IPS),Wm=FS(process.env.BOSTRAT_VIEW_FRONT_CIDRS,gl),Jv=HS(),Yv=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",S=me();S.disable("x-powered-by");S.use((t,e,n)=>{e.setHeader("x-content-type-options","nosniff"),wl(t.headers.host)||(e.setHeader("x-frame-options","DENY"),e.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});S.use((t,e,n)=>{if(ja(t)||os.allowed(os.clientIp(t))||Wm.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(VS);S.use(KS({token:Jv,enforce:Yv}));S.use("/api/mocks",me.json({limit:"1mb"}));S.use("/api/mocks",me.raw({type:"image/png",limit:"24mb"}));S.use("/api/mocks",me.raw({type:["video/mp4","video/webm"],limit:"105mb"}));S.use("/api/runbooks",me.json({limit:"1mb"}));S.use("/api/tracks/adopt",me.json({limit:"1mb"}));S.use("/api/telemetry",me.json({limit:"256kb"}));S.use("/api/hook",me.json({limit:"2mb"}));S.use("/api/dictation",me.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));S.use("/api/tracks/:id/upload",me.raw({type:()=>!0,limit:"25mb"}));S.use(me.json({limit:"64kb"}));S.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:ee,hostTmuxAlive:await Sn(),autoTriggerPct:zv})});S.get("/api/sessions",async(t,e)=>{e.json({sessions:await we()})});S.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 vn(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})}});S.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!St(n))return e.status(400).json({error:"invalid session name"});if(!await ot(n))return e.status(404).json({error:"session not found"});try{await Ie(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});S.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!St(n))return e.status(400).json({error:"invalid session name"});let r=await ot(n);if(!r)return e.status(404).json({error:"session not found"});let o=await xs(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});S.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!St(n))return e.status(400).json({error:"invalid session name"});try{let r=await Ag(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)})}});S.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=Di(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 Wv=10;S.get("/api/idle-guard",async(t,e)=>{try{e.json(Sx({tracks:await at(),views:await ff(),queuedOf:n=>Uo(n)}))}catch{e.status(500).json({busy:!0,reasons:["error"]})}});S.get("/api/tracks",async(t,e)=>{let n=(await at()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>Wv){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,Wv).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:ba,agentAvailability:await Nc(),tickets:await Wx()})});S.get("/api/cost",async(t,e)=>{try{let n=await at();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await kS(n);if(r.cursorUsage=Zd(),t.query.format==="text")return e.type("text/plain").send(xS(r));e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/roundup",async(t,e)=>{try{e.json(await TS(await at(),{window:t.query.window,from:t.query.from,to:t.query.to}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/spend",async(t,e)=>{try{let n=await RS(await at(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=Zd(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=js(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(t,e)=>{e.json(js())});S.post("/api/usage/caps",async(t,e)=>{try{e.json(await Vi(t.body?.caps??{},{source:"local"}))}catch(n){e.status(400).json({error:n?.message||String(n)})}});S.get("/api/usage/limits",async(t,e)=>{try{let n=t.query.fresh==="1"||t.query.fresh==="true";e.json(await NS(await at(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/hook",async(t,e)=>{try{let{mode:n,target:r}=await Vm(t.get(Ya),{byToken:ip,legacy:()=>Vs(t.body?.session_id,t.body?.cwd)});if(n==="refused")return as("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return as("legacy-denied",{reason:"tokenless-denied",hook:t.body?.hook_event_name}),e.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&t.body?.session_id&&r.box?.sessionId&&t.body.session_id!==r.box.sessionId&&as("mismatch",{track:r.track.id,box:r.box.name}),as(n,{track:r?.track?.id,hook:t.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await dS(t.body||{},()=>r);e.json({ok:!0,events:o.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});S.post("/api/telemetry",async(t,e)=>{try{if(!Tv(os.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await vv(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/repos",async(t,e)=>{try{let n=await He(),r=new Set((await Promise.all(n.map(a=>lt(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await ds()).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)})}});S.get("/api/github/token",async(t,e)=>{try{e.json(await ni(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/github/status",(t,e)=>{e.json({configured:ei(),calls:gb(),latency:kk()})});S.post("/api/github/refresh",(t,e)=>{rh(),e.json({ok:!0})});S.get("/api/gmail/search",async(t,e)=>{try{let n=await BS(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)})}});S.get("/api/gmail/messages/:id",async(t,e)=>{try{e.json({message:await DS(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function qm(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 El({repo:t=null,number:e=null,branch:n=null}){let r=t?await lt(t):null;if(r&&e!=null){let s=await qt(r,e),a=s&&await Q(s.trackId);if(a)return a}return n?(await at()).find(s=>s.workspace?.branch===n&&qm(s,t,r))??null:null}async function Xv({repo:t=null,number:e=null,branch:n=null}){let r=await El({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=t?await lt(t).catch(()=>null):null;return(await at()).find(a=>!a.archivedAt&&!a.transferredTo&&a.workspace?.branch===n&&qm(a,t,o))??null}async function Te(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await El({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await lt(s).catch(()=>null):null);await $({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function Y1(t){let e=(await at()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of t??[]){if(!n.prs?.length)continue;let r=await lt(n.path);if(r)for(let o of n.prs){if(await qt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&qm(a,n.path,r));s&&await re({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}S.get("/api/git/prs",async(t,e)=>{try{let n=await _k();await Y1(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=t.body||{};try{if(s){let w=o||"merge";if(!ta.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let k=await lt((await en(n)).path);if(!k)throw new Error("repo has no GitHub remote");let x=await El({repo:n,number:y,branch:l}).catch(()=>null);if(!x)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await ea(n,y),await re({repo:k,number:y,trackId:x.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Hs(k,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Er(k,y,i===!0),await Ws(k,y,c===!0&&i!==!0),Ne(),await Te("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:x,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:y,auto:!0})}let d=await Oo(n,r,{method:o||"squash"});await Te("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await lt(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Er(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Ws(p,d.number,!0).catch(()=>{}),await Xe(p,d.number).catch(()=>{}),m=await Ye(p,d.number).catch(()=>!1),await Dn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let w=await El({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Br(w.id).catch(()=>{})}e.json(d)}catch(d){let p=d?.message||String(d),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;e.status(m).json({error:p})}});S.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 lt((await en(n)).path).catch(()=>null),l=c?await qs(c,i).catch(()=>null):null;try{await xf(n,i)}catch(u){if(!l)throw u}Ne(),await Te("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})}});S.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await lt(n).catch(()=>null),s=await Ok(n,{method:r||"merge",onMerged:a=>{Te("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Xe(o,a.number).catch(()=>{}).then(()=>Ye(o,a.number)).catch(()=>{}).then(()=>Dn(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})}});S.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:$k()})});S.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(!ta.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 _r()).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 Er(d.repo,d.number,o===!0).catch(()=>{}),await Ws(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 $r(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let w=await $o(m,d.number);f.push({r:p,l:d,repoPath:m,pr:w,plan:Ik(w,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:d,l:p,repoPath:m,pr:w,plan:y}of Rk(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Oo(m,p.number,{method:i}),await Te("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await Xe(p.repo,p.number).catch(()=>{}),await Ye(p.repo,p.number).catch(()=>{}),await Dn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await ea(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Hs(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await Te("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){d.ok=!1,d.action="failed",d.reason=k?.message||String(k)}if(Ne(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Er(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await Ws(d.repo,d.number,!1).catch(()=>{});return e.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}e.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;e.status(l).json({error:c})}});S.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 _r()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await qs(a.repo,a.number).catch(()=>null),c=await $r(a.repo).catch(()=>null);c&&await xf(c,a.number).catch(()=>{}),i&&(await Te("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}))}Ne(),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})}});S.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=se(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let y=u.find(k=>k.name===w||k.repo===w||k.base===w||k.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(f=await Eo(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let y=await vf(w.dir,{title:r,body:o,base:s}),k=w.repo||await lt(w.dir).catch(()=>null);k&&y.number&&await re({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Te("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??w.name}}),p.push({repo:w.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(w=>w.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await vf(i.cwd,{title:r,body:o,base:s}),l=await lt(i.cwd).catch(()=>null);l&&c.number&&await re({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Te("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})}});S.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 Eo(n),o=new Map(r.map(a=>[a.base,a])),s=se(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)})}});S.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 gp(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})}});S.delete("/api/tracks/:id/repos/:name",async(t,e)=>{try{let n=await yp(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})}});S.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await $o(n,r),s=await lt(n).catch(()=>null),a=await Xv({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=se(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await Ck(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?mt.join("ci",i.name):"ci",f=mt.join(a.cwd,".bostrat",u),d=mt.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ft.promises.mkdir(f,{recursive:!0}),await Ft.promises.writeFile(mt.join(a.cwd,d),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
481
486
|
${k.log??"(no fetchable log \u2014 see link)"}
|
|
482
487
|
`).join(`
|
|
483
|
-
`));let p=l.map(k=>k.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await fe(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${f} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await ne({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await nb(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(k=>k.name)}}),e.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(k=>k.name),log:f})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});x.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await xo(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await st(n).catch(()=>null),a=await Rv({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=pe(a.workspace).find(d=>d.base===n||s&&d.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await fe(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 ne({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await rb(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=Fi.filter(d=>t.body.actions.includes(d)),!o.length)return e.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return e.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=o!=null&&t.body?.notify===!0,a=await z(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await L(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return e.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await am(a,c,{actions:o,notify:s});if(d)return e.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await fe(a.id,mo(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await I({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&zf(a.id,{actions:o}),e.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});x.post("/api/tracks/:id/pr-exists",async(t,e)=>{let{offerId:n,choice:r}=t.body||{};try{let o=await z(t.params.id);if(!o)return e.status(404).json({error:"track not found"});let{say:s}=await im(o,{offerId:n,choice:r});e.json({ok:!0,offerId:n,choice:r,queued:!!s?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/unknown offer/i.test(s)?404:/already answered|not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});var Da=t=>e=>({...e,url:_S(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await Qd({trackId:t.query.track||void 0});e.json({views:n.map(Da(t)),maxLive:Ln})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await Gb(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Da(t)(n),session:yo(n.slug)})});x.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await z(n):null;if(!i&&s&&(i=(await et()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ut.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await ef({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:Da(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:Da(t)(await Vb(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:Da(t)(await zb(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 Jb(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var E1=[["linear",Or,kp],["jira",jc,yp]],hm=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await gp(),r={};for(let[o,s,a]of E1){let i=await s();if(!i){hm.delete(o),r[o]={configured:!1};continue}let c=hm.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},hm.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 gp()})}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 nx(o),i=n?await z(n):null;!i&&r&&(i=(await et()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ut.sep)))||null),i&&await I({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var Ba=async(t,e)=>{let n=t?await z(t):null;return!n&&e&&(n=(await et()).find(o=>o.cwd&&(e===o.cwd||e.startsWith(o.cwd+ut.sep)))||null),n};x.get("/api/circuits",async(t,e)=>{try{e.json({circuits:await pp()})}catch(n){e.status(502).json({error:n?.message||String(n)})}});x.post("/api/circuits/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=await Ba(n,r);if(!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (circuit name or id) required"});let a=await Bo(o);await Pc(s,a,{source:"chat"}),e.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);e.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});x.post("/api/circuits/unlink",async(t,e)=>{let{trackId:n,cwd:r}=t.body||{};try{let o=await Ba(n,r);if(!o)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Pc(o,null,{source:"chat"}),e.json({ok:!0,trackId:o.id})}catch(o){e.status(502).json({error:o?.message||String(o)})}});var $v=async(t,e,n)=>{if(t)return{ref:t,track:await Ba(e,n)};let r=await Ba(e,n),o=r?await hp(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};x.post("/api/blueprints/publish",async(t,e)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=t.body||{};try{if(!i||!String(i).trim())return e.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await $v(o,n,r),l=await X0({circuitRef:c,newCircuitName:s,name:a,markdown:i});e.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);e.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});x.get("/api/blueprints/show",async(t,e)=>{try{let{ref:n}=await $v(t.query.circuit,t.query.track,t.query.cwd);e.json(await mp(n))}catch(n){let r=n?.message||String(n);e.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});x.post("/api/blueprints/tag",async(t,e)=>{let{trackId:n,cwd:r,itemIds:o}=t.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return e.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await Ba(n,r);if(!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await hp(a.id);if(!i)return e.status(400).json({error:"this Track isn't in a circuit \u2014 link one first (bostrat-circuit link \u2026)"});let c=await Z0(a,i,s);e.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);e.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await z(n):null;if(!s&&r&&(s=(await et()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ut.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 Lo(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});x.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await z(r):null;if(!o&&n&&(o=(await et()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ut.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 yx(),ticket:await Fe(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 Fe(t.params.id)})});x.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await z(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Lo(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 Cp(t.params.id)})});var wl=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 Sx(t.params.id))}catch(n){let r=n?.message||String(n);e.status(wl(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await Mp(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(wl(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await vx(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(wl(r)).json({error:r})}});x.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await z(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await Tx(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(wl(s)).json({error:s})}});x.post("/api/tracks/:id/fullscreen-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{e.json(await sp(t.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;e.status(a).json({error:s})}});x.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await z(n):null;if(!s&&r&&(s=(await et()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ut.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 Ex(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 Ax()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});x.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await z(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await _x(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 Xs({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=ke(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Js(n,r,"png");if(!o)return e.status(404).json({error:"no image for this version (poster extraction failed)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/media",async(t,e)=>{let n=await en(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ke(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Js(n,r,"video");if(!o)return e.status(404).json({error:"this version has no video (image or html sourced)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"video artifact missing"})})});x.get("/api/mocks/:id/html",async(t,e)=>{let n=await en(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=ke(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(le(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&&pc(n)?n:null,o=n&&!r?n:null,s=n?t.query:t.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let d=a?await z(a):null;if(!d&&c&&(d=(await et()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+ut.sep)))||null),!d)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=(d.boxes||[]).find(m=>m.name===d.leadBox),p=await Ek({trackId:d.id,name:i,html:n?null:s.html,image:r,video:o,cwd:d.cwd,width:l,group:u,box:d.leadBox,agent:f?.agent||null});e.json({ok:!0,mock:p})}catch(d){let f=d?.message||String(d);e.status(/required|invalid|must be/i.test(f)?400:500).json({error:f})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&pc(n)?n:null;try{let o=await _k(t.params.id,{html:n?null:(t.body||{}).html,image:r,video:n&&!r?n:null,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;e.status(a).json({error:s})}});x.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await $k(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await fe(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 zk(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Ok(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 Wk({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 qk(t.params.id);if(!n)return e.status(404).json({error:"flow not found"});e.json({flow:n})});x.post("/api/flows",async(t,e)=>{let{trackId:n,name:r,steps:o,cwd:s}=t.body||{};try{let a=n?await z(n):null;if(!a&&s&&(a=(await et()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ut.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 Kk({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 Gk(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 Vk(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let s=await et(),a=String(t.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+ut.sep)))?.id,!n)return e.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await n0({trackId:n,status:t.query.status||void 0});e.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/runbooks/:id",async(t,e)=>{let n=await Af(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 Af(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=t0(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});if(e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),n.remote){let o=await r0(n,r.v);return typeof o!="string"?e.status(404).json({error:"runbook artifact missing"}):e.send(o)}e.sendFile(sn(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});x.get("/api/loops",async(t,e)=>{e.json(await Qp())});x.post("/api/loops/report",async(t,e)=>{let{outcome:n,note:r,cwd:o}=t.body||{};if(!["ok","acted","attention"].includes(n))return e.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return e.status(400).json({error:"cwd required"});let a=(await et()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+ut.sep)))||null;if(!a)return e.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Qp();if(i.source!=="plane")return e.status(503).json({error:i.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${i.error||"no detail"})`});let c=i.loops.find(u=>u.trackId===a.id);if(!c)return e.status(404).json({error:`Track "${a.name}" is not a loop's run Track \u2014 nothing to report on`});let l=await OS({loopId:c.id,outcome:n,note:r});if(!l.ok)return e.status(l.status||503).json({error:l.error||"report failed"});e.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});x.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await z(n):null;if(!a&&s&&(a=(await et()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ut.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 o0({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 s0(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 a0(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 i0(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=PS(t.query.since||null);e.json({since:n,repos:tm(),prs:await NS(n)})}catch(n){let r=n?.message||String(n);e.status(/unparseable/i.test(r)?400:500).json({error:r})}});x.post("/api/changelog/publish",async(t,e)=>{let{md:n,day:r,trackId:o,cwd:s}=t.body||{};try{let a=o?await z(o):null;if(!a&&s&&(a=(await et()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+ut.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await BS(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 _1=120,wm=400,hl=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>wm)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${wm} cap; a notification is a nudge, put the detail in chat`});let c=Zs(`${n||""}
|
|
484
|
-
${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
|
|
488
|
+
`));let p=l.map(k=>k.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await pe(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 re({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await xb(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Te("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),e.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(k=>k.name),log:d})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});S.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await $o(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await lt(n).catch(()=>null),a=await Xv({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=se(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 pe(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 re({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Sb(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Te("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})}});S.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=Zi.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 U(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"});if(o?.includes("pr")){let f=await $m(a,c,{actions:o,notify:s});if(f)return e.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:f.pr})}let l=null;o&&(l=await pe(a.id,vo(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await $({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&fp(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})}});S.post("/api/tracks/:id/pr-exists",async(t,e)=>{let{offerId:n,choice:r}=t.body||{};try{let o=await Q(t.params.id);if(!o)return e.status(404).json({error:"track not found"});let{say:s}=await Om(o,{offerId:n,choice:r});e.json({ok:!0,offerId:n,choice:r,queued:!!s?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/unknown offer/i.test(s)?404:/already answered|not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});var Va=t=>e=>({...e,url:JS(e,t.headers)});S.get("/api/views",async(t,e)=>{try{let n=await ff({trackId:t.query.track||void 0});e.json({views:n.map(Va(t)),maxLive:Un})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(t,e)=>{let n=await dk(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Va(t)(n),session:_o(n.slug)})});S.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 at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+mt.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await mf({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:Va(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})}});S.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:Va(t)(await fk(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:Va(t)(await pk(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})}});S.delete("/api/views/:id",async(t,e)=>{try{e.json(await mk(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var X1=[["linear",jr,qp],["jira",Xc,Hp]],Lm=new Map;S.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await Up(),r={};for(let[o,s,a]of X1){let i=await s();if(!i){Lm.delete(o),r[o]={configured:!1};continue}let c=Lm.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},Lm.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)})}});S.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await Up()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await Ix(o),i=n?await Q(n):null;!i&&r&&(i=(await at()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+mt.sep)))||null),i&&await $({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var Ga=async(t,e)=>{let n=t?await Q(t):null;return!n&&e&&(n=(await at()).find(o=>o.cwd&&(e===o.cwd||e.startsWith(o.cwd+mt.sep)))||null),n};S.get("/api/circuits",async(t,e)=>{try{e.json({circuits:await Dp()})}catch(n){e.status(502).json({error:n?.message||String(n)})}});S.post("/api/circuits/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=await Ga(n,r);if(!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (circuit name or id) required"});let a=await Go(o);await Vc(s,a,{source:"chat"}),e.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);e.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});S.post("/api/circuits/unlink",async(t,e)=>{let{trackId:n,cwd:r}=t.body||{};try{let o=await Ga(n,r);if(!o)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Vc(o,null,{source:"chat"}),e.json({ok:!0,trackId:o.id})}catch(o){e.status(502).json({error:o?.message||String(o)})}});var Zv=async(t,e,n)=>{if(t)return{ref:t,track:await Ga(e,n)};let r=await Ga(e,n),o=r?await Lp(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};S.post("/api/blueprints/publish",async(t,e)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=t.body||{};try{if(!i||!String(i).trim())return e.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await Zv(o,n,r),l=await vx({circuitRef:c,newCircuitName:s,name:a,markdown:i});e.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);e.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});S.get("/api/blueprints/show",async(t,e)=>{try{let{ref:n}=await Zv(t.query.circuit,t.query.track,t.query.cwd);e.json(await jp(n))}catch(n){let r=n?.message||String(n);e.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});S.post("/api/blueprints/tag",async(t,e)=>{let{trackId:n,cwd:r,itemIds:o}=t.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return e.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await Ga(n,r);if(!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Lp(a.id);if(!i)return e.status(400).json({error:"this Track isn't in a circuit \u2014 link one first (bostrat-circuit link \u2026)"});let c=await Tx(a,i,s);e.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);e.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});S.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 at()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+mt.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await Jo(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});S.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 at()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+mt.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 qx(),ticket:await Ue(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});S.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await Ue(t.params.id)})});S.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 Jo(n,(t.body||{}).ref,{source:"api"});e.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);e.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});S.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await nm(t.params.id)})});var Il=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;S.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await zx(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Il(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await rm(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Il(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Jx(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Il(r)).json({error:r})}});S.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 Yx(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Il(s)).json({error:s})}});S.post("/api/tracks/:id/fullscreen-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{e.json(await Ap(t.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;e.status(a).json({error:s})}});S.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 at()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+mt.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 Zx(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})}});S.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await Xx()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});S.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 Qx(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})}});S.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await sa({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.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})});S.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=Se(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=ra(n,r,"png");if(!o)return e.status(404).json({error:"no image for this version (poster extraction failed)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(t,e)=>{let n=await nn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Se(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=ra(n,r,"video");if(!o)return e.status(404).json({error:"this version has no video (image or html sourced)"});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(o,s=>{s&&!e.headersSent&&e.status(404).json({error:"video artifact missing"})})});S.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=Se(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(de(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});S.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&Ac(n)?n:null,o=n&&!r?n:null,s=n?t.query:t.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await Q(a):null;if(!f&&c&&(f=(await at()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+mt.sep)))||null),!f)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(m=>m.name===f.leadBox),p=await Kk({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});e.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);e.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});S.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&Ac(n)?n:null;try{let o=await Gk(t.params.id,{html:n?null:(t.body||{}).html,image:r,video:n&&!r?n:null,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;e.status(a).json({error:s})}});S.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Jk(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await pe(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})}});S.delete("/api/mocks/:id",async(t,e)=>{try{let n=(await p0(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Yk(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})}});S.get("/api/flows",async(t,e)=>{try{e.json({flows:await c0({trackId:t.query.track||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/flows/:id",async(t,e)=>{let n=await l0(t.params.id);if(!n)return e.status(404).json({error:"flow not found"});e.json({flow:n})});S.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 at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+mt.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 u0({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})}});S.post("/api/flows/:id/update",async(t,e)=>{try{let{steps:n,note:r}=t.body||{};e.json({ok:!0,flow:await d0(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})}});S.delete("/api/flows/:id",async(t,e)=>{try{e.json(await f0(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let s=await at(),a=String(t.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+mt.sep)))?.id,!n)return e.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await x0({trackId:n,status:t.query.status||void 0});e.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/runbooks/:id",async(t,e)=>{let n=await Df(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});S.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Df(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=b0(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});if(e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),n.remote){let o=await S0(n,r.v);return typeof o!="string"?e.status(404).json({error:"runbook artifact missing"}):e.send(o)}e.sendFile(an(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(t,e)=>{e.json(await vm())});S.post("/api/loops/report",async(t,e)=>{let{outcome:n,note:r,cwd:o}=t.body||{};if(!["ok","acted","attention"].includes(n))return e.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return e.status(400).json({error:"cwd required"});let a=(await at()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+mt.sep)))||null;if(!a)return e.status(400).json({error:"cwd is not inside a Track workspace"});let i=await vm();if(i.source!=="plane")return e.status(503).json({error:i.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${i.error||"no detail"})`});let c=i.loops.find(u=>u.trackId===a.id);if(!c)return e.status(404).json({error:`Track "${a.name}" is not a loop's run Track \u2014 nothing to report on`});let l=await QS({loopId:c.id,outcome:n,note:r});if(!l.ok)return e.status(l.status||503).json({error:l.error||"report failed"});e.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});S.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 at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+mt.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 v0({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})}});S.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await T0(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})}});S.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await A0(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})}});S.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await E0(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/changelog/draft",async(t,e)=>{try{let n=nv(t.query.since||null);e.json({since:n,repos:Tm(),prs:await rv(n)})}catch(n){let r=n?.message||String(n);e.status(/unparseable/i.test(r)?400:500).json({error:r})}});S.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 at()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+mt.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await ov(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 Z1=120,Fm=400,_l=null;S.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>Fm)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Fm} cap; a notification is a nudge, put the detail in chat`});let c=aa(`${n||""}
|
|
489
|
+
${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 at()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+mt.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,Z1):l.name,body:f(i,Fm),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await $(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),e.json({ok:!0,notification:d,delivered:_l?_l.status().connected:null})}catch(i){e.status(500).json({error:i?.message||String(i)})}});S.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 jv(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});S.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await Ea(),terms:await Dm(),problem:await Dv()})});S.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 Bv(n,r)})});S.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 Lv())});S.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 Fv())}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});S.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 Uv(n,{model:r,usage:o,seconds:s})})}catch(n){e.status(n?.status||500).json({error:n?.message||String(n)})}});S.post("/api/tracks",async(t,e)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c,circuit:l}=t.body||{};try{let u=await jc({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),f=null;if(c)try{await Jo(u,c,{source:"create"})}catch(p){f=p?.message||String(p)}let d=null;if(l)try{await Vc(u,await Go(l),{source:"picker"})}catch(p){d=p?.message||String(p)}e.json({ok:!0,track:u,...f?{ticketWarning:f}:{},...d?{circuitWarning:d}:{}})}catch(u){let f=u?.message||String(u);e.status(/required|unknown agent|invalid|must be/i.test(f)?400:500).json({error:f})}});S.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:Qv(Ko(t.params.id)),subtasks:Dr(t.params.id).map(za)})});S.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Sp(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await nm(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})}});S.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await Br(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})}});S.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await vp(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})}});S.get("/api/tracks/:id/messages",async(t,e)=>{let n=await Pc(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});S.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}
|
|
485
490
|
data: ${JSON.stringify(i)}
|
|
486
491
|
|
|
487
|
-
`),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await
|
|
492
|
+
`),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await U(n))i.seq>o&&r(i);let s=Tw(n.id,r),a=setInterval(()=>e.write(`: hb
|
|
488
493
|
|
|
489
|
-
`),25e3);t.on("close",()=>{clearInterval(a),s()})});
|
|
490
|
-
`);
|
|
494
|
+
`),25e3);t.on("close",()=>{clearInterval(a),s()})});S.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=Is(await U(n)),a=Ri(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}))})});S.get("/api/memory",async(t,e)=>{e.json({stores:await yh()})});S.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 bh(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 Q1=["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(`
|
|
495
|
+
`);S.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await pe(t.params.id,Q1)})}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})}});S.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await Uc(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})}});S.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await pe(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return e.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});S.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 C0(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})}});S.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=mt.basename(String(t.params.file||""));if(!r||!Ff(mt.extname(r).slice(1)))return e.status(400).json({error:"not an upload path"});e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox");let o=Lf(n.cwd),s,a;try{s=await Ft.promises.realpath(o),a=await Ft.promises.realpath(mt.join(o,r))}catch{return e.status(404).json({error:"upload not found"})}if(!a.startsWith(s+mt.sep))return e.status(404).json({error:"upload not found"});e.sendFile(r,{root:o},i=>{i&&!e.headersSent&&e.status(404).json({error:"upload not found"})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});var tj=/^[0-9a-fA-F-]{8,64}$/;S.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(!tj.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=Ke(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=K1.createInterface({input:Ft.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?au(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)})}});S.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await Dc(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})}});S.delete("/api/tracks/:id/queue",async(t,e)=>{try{let{removed:n,restored:r,draftRev:o}=await dp(t.params.id);e.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await Bc(t.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code|no answer selected/i.test(a)?409:500;e.status(i).json({error:a})}});S.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await kp({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})}});S.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await lp(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})}});S.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n,rev:r}=t.body||{};try{e.json(await hp(t.params.id,typeof n=="string"?n:"",{rev:typeof r=="number"?r:null}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required/i.test(s)?400:500;e.status(a).json({error:s})}});S.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 pl(r,{fresh:o}))});S.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await wp(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})}});S.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await Np(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:za(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return e.status(429).json({error:"usage_cap",message:i,cap:a.usageCap});let c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;e.status(c).json({error:i})}});S.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:Dr(t.params.id).map(za)})});S.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await cp(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});S.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:za(Gc(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});S.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:za(Pp(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function za(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}S.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await Wc(t.params.id,n,r!==!1,o);e.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);if(s?.usageCap)return e.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});S.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await pp(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})}});S.get("/api/tracks/:id/handoff-lead",(t,e)=>{e.json(Qv(Ko(t.params.id))??{running:!1})});S.post("/api/tracks/:id/transfer-out",async(t,e)=>{try{let n=await Rp(t.params.id);e.json({ok:!0,step:n.step,startedAt:n.startedAt})}catch(n){let r=n?.message||String(n);e.status(/already running/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});S.get("/api/tracks/:id/transfer-out",(t,e)=>{let n=Ip(t.params.id);if(!n)return e.json({running:!1});e.json({running:!n.finishedAt,step:n.step,error:n.error,startedAt:n.startedAt,finishedAt:n.finishedAt,...n.result??{}})});S.post("/api/tracks/:id/transfer-out/commit",async(t,e)=>{try{e.json(await qc(t.params.id,t.body||{}))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:400).json({error:r})}});S.post("/api/tracks/adopt",async(t,e)=>{let{trackId:n}=t.body||{};try{e.json(await Op({...t.body||{},trackId:n}))}catch(r){let o=r?.message||String(r);e.status(/already running|already hosted/i.test(o)?409:/not found/i.test(o)?404:400).json({error:o})}});function Qv(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}S.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:pi(),paths:Ph,trace:await Nh({limit:n})})});var Um=mt.resolve(Vv,"..","..","client","dist");Ft.existsSync(Um)&&(S.use(me.static(Um)),S.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(mt.join(Um,"index.html"))));var tT=V1.createServer(S),ej=new J1({noServer:!0}),eT=(t,e,n)=>{if(!(ja(t)||os.allowed(os.clientIp(t))||Wm.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
|
|
491
496
|
\r
|
|
492
|
-
`),e.destroy();return}if(
|
|
497
|
+
`),e.destroy();return}if(zS(t,e,n))return;if(!GS(t,Jv,{enforce:Yv})){e.write(`HTTP/1.1 401 Unauthorized\r
|
|
493
498
|
\r
|
|
494
|
-
`),e.destroy();return}let r=
|
|
499
|
+
`),e.destroy();return}let r=zh(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
495
500
|
\r
|
|
496
|
-
`),e.destroy();return}let o=r.sessionName;if(!
|
|
501
|
+
`),e.destroy();return}let o=r.sessionName;if(!St(o)){e.write(`HTTP/1.1 400 Bad Request\r
|
|
497
502
|
\r
|
|
498
|
-
`),e.destroy();return}let{cols:s,rows:a}=r
|
|
503
|
+
`),e.destroy();return}let{cols:s,rows:a}=r;ej.handleUpgrade(t,e,n,i=>Kh(i,o,{cols:s,rows:a}))};tT.on("upgrade",eT);var nT=process.env.BOSTRAT_CERT_DIR||mt.join(process.env.HOME||Hm.tmpdir(),".bostrat","certs"),qv=Number(process.env.BOSTRAT_TLS_PORT)||3443,Kv=mt.join(nT,"bostrat.globopi.key"),Gv=mt.join(nT,"bostrat.globopi.crt");if(Ft.existsSync(Kv)&&Ft.existsSync(Gv)){let t=z1.createServer({key:Ft.readFileSync(Kv),cert:Ft.readFileSync(Gv)},S);t.on("upgrade",eT),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(qv,Al,()=>{console.log(`[bostrat-agent] https listening on ${Al||"0.0.0.0"}:${qv}`)})}tT.listen(Hv,Al,()=>{console.log(`[bostrat-agent] listening on ${Al||"0.0.0.0"}:${Hv}`);let t=fS(process.env);t&&console.warn(`[bostrat-agent] \u26A0 SECURITY: plane-linked node bound to ${t.host?t.host:"ALL interfaces (BOSTRAT_HOST=all)"} \u2014 the API + Views are reachable off-loopback (LAN/VPN), where the IP allowlist is spoofable and Views need no token. The plane tunnel does not require this; set BOSTRAT_HOST=127.0.0.1 once any local reverse-proxy/WireGuard path is retired (F-2).`),console.log(`[bostrat-agent] tmux socket: ${ee}`),console.log(`[bostrat-agent] auto-trigger default: ${zv}%`),console.log(`[bostrat-agent] ip allowlist: ${os.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Wm.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),_p().then(e=>e&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(e=>console.error("[bostrat-agent] handoff reconcile failed:",e?.message||e)),Cp().then(e=>e&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(e=>console.error("[bostrat-agent] subtask reconcile failed:",e?.message||e)),Tp().then(e=>e&&console.log(`[bostrat-agent] reaped ${e} orphaned box(es) on archived track(s)`)).catch(e=>console.error("[bostrat-agent] archived-orphan sweep failed:",e?.message||e)),bp().then(e=>console.log(`[bostrat-agent] standing instructions refreshed (${e.refreshed} updated, ${e.removed} legacy CLAUDE.md removed${e.codexGlobal?`, codex global ${e.codexGlobal}`:""})`)).catch(e=>console.error("[bostrat-agent] standing-instructions refresh failed:",e?.message||e)),sj(),oj(),Py({listTracks:at,getTrack:Q}).then(e=>console.log(`[bostrat-agent] spend caps ${e.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(e.spent).length} provider counter(s)`)).catch(e=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",e?.message||e)),D0()&&j0({listTracks:at}),uv(),pv(),tS({resolveTrack:Q}),uS({listTracks:at,createTrack:jc,archiveTrack:Br,listRepos:He,resolveTrack:Q,sayToLead:pe,answerPrompt:Bc,startLeadHandoff:Wc,interruptLead:Dc,listSubtaskJobs:Dr,cancelSubtask:Gc,readEvents:U,appendEvent:$,divertShip:$m,answerPrExists:Om,knownAgents:ba}),nj(),bs().then(e=>{e||console.warn(`[boot] no host tmux server on ${ee} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),_v(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(_l=hS({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Hm.hostname(),name:process.env.BOSTRAT_NODE_NAME||Hm.hostname(),listTracks:at,detectAgents:Nc,sampleMetrics:gS,onTracksTransferred:$p}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),zr().catch(()=>{}),R0().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} runbooks to the plane`),()=>{}),eS().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} ticket links to the plane`),()=>{}),Qk().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} mocks to the plane`),()=>{}),w0().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} flows to the plane`),()=>{}),Ib().then(e=>e.pushed&&console.log(`[content-plane] seeded ${e.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{_l?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function nj(){rj(),wc().catch(()=>{}),setInterval(()=>wc().catch(()=>{}),1e4)}function rj(){for(let t of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-runbook","bostrat-loop","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let e=mt.resolve(Vv,"..","bin",t),n=mt.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Ft.existsSync(e))continue;Ft.mkdirSync(n,{recursive:!0});let r=mt.join(n,t);(Ft.existsSync(r)?Ft.readlinkSync(r):null)!==e&&(Ft.rmSync(r,{force:!0}),Ft.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 oj(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await at(),r=await OS({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 sj(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await at();for(let n of e)n.archivedAt||n.transferredTo||!n.boxes?.some(r=>r.state==="live")||(await Pc(n.id).catch(()=>{}),await Mk(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
|