bostrat-node 1.7.3 → 1.7.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 +171 -171
- 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 YT=Object.defineProperty;var O=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var zl=(e,t)=>{for(var n in t)YT(e,n,{get:t[n],enumerable:!0})};import{createHash as XT,randomBytes as ZT,timingSafeEqual as QT}from"node:crypto";function ph(){return ZT(32).toString("hex")}function Ss(e){let t=typeof e=="string"?e.trim():"";return t?XT("sha256").update(t).digest("hex"):null}function mh(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:QT(Buffer.from(e),Buffer.from(t))}function Ts(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(Jl.set(e,(Jl.get(e)||0)+1),Yl.has(e)&&n-Yl.get(e)<eA)return!1;Yl.set(e,n);let o=Object.entries(t).filter(([,s])=>s!=null&&s!=="").map(([s,a])=>`${s}=${a}`).join(" ");return r(`[hook-audit] ${e} n=${Jl.get(e)}${o?` ${o}`:""}`),!0}async function hh(e,{byToken:t,legacy:n,env:r=process.env}={}){let o=typeof e=="string"?e.trim():"";if(!o)return r.BOSTRAT_HOOK_LEGACY_DENY==="1"?{mode:"legacy-denied",target:null}:{mode:"legacy",target:await n()};let s=await t(o);return s?.track?{mode:"token",target:s}:{mode:"refused",target:null}}var fi,pi,Jl,Yl,eA,As=O(()=>{fi="BOSTRAT_HOOK_TOKEN",pi="x-bostrat-hook";Jl=new Map,Yl=new Map,eA=6e4});import ot from"node:fs/promises";import wh from"node:os";import Ze from"node:path";import{execFile as tA}from"node:child_process";import{promisify as nA}from"node:util";function mi(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function rA(){return process.env.CLAUDE_CONFIG_DIR?Ze.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Ze.join(wh.homedir(),".claude.json")}function oA(){return process.env.CLAUDE_CONFIG_DIR?Ze.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Ze.join(wh.homedir(),".claude","settings.json")}async function En(e){if(!e||typeof e!="string")return;let t=Ze.resolve(e),n=rA(),r=oA(),o={};try{o=JSON.parse(await ot.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[t]=o.projects[t]||{};let s=o.projects[t];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 ot.mkdir(Ze.dirname(n),{recursive:!0}),await ot.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await ot.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 "${pi}: $${fi}" --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 ot.mkdir(Ze.dirname(r),{recursive:!0}),await ot.writeFile(r,JSON.stringify(i,null,2)),await sA(t)}async function sA(e){try{let t=Ze.resolve(e),n=Ze.join(t,".bostrat","memory","claude"),r=Ze.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await ot.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Ze.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await ot.mkdir(n,{recursive:!0}),await ot.mkdir(Ze.dirname(r),{recursive:!0}),await ot.writeFile(r,JSON.stringify(o,null,2)),await aA(t,["/.claude/settings.local.json"])}catch{}}async function aA(e,t){try{let{stdout:n}=await nA(tA)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=Ze.resolve(e,n.trim()),o=await ot.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await ot.mkdir(Ze.dirname(r),{recursive:!0}),await ot.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var Es=O(()=>{As()});import Xl from"node:fs/promises";import iA from"node:os";import hi from"node:path";function cA(){let e=process.env.CODEX_HOME||hi.join(iA.homedir(),".codex");return hi.join(e,"config.toml")}function gh(e){return`[projects."${e}"]`}function lA(e,t){let n=gh(t),r=e.indexOf(n);if(r===-1)return!1;let o=e.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 to(e){if(!e||typeof e!="string")return;let t=hi.resolve(e),n=cA(),r="";try{r=await Xl.readFile(n,"utf8")}catch{}if(lA(r,t))return;let o=gh(t);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
|
|
15
|
-
`),s=new Map;for(let c of(
|
|
16
|
-
`)){let l=
|
|
13
|
+
`:"")}try{await Xl.mkdir(hi.dirname(n),{recursive:!0}),await Xl.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var wi=O(()=>{});import yh from"node:fs/promises";import uA from"node:os";import _s from"node:path";function dA(e){let n=_s.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||_s.join(uA.homedir(),".cursor");return _s.join(r,"projects",n)}async function no(e){if(!e||typeof e!="string")return;let t=_s.resolve(e),n=dA(t),r=_s.join(n,".workspace-trusted");await yh.mkdir(n,{recursive:!0}),await yh.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var gi=O(()=>{});import Ql from"node:fs/promises";import _n from"node:path";import{execFile as fA}from"node:child_process";import{promisify as pA}from"node:util";async function bA(e){try{return(await Ql.stat(_n.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function kA(e){let t=i=>Ql.readFile(_n.join(e,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await t("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 t(`refs/heads/${i}`)||(o??=await t("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await t("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function zt(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await bA(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:_n.basename(o),branch:await kA(o)});return}if(s>=gA)return;let i;try{i=await Ql.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||yA.has(c.name)||await t(_n.join(o,c.name),s+1)}for(let o of wA)await t(_n.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await de(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 xA(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function de(e){if(!e)return null;if(Zl.has(e))return Zl.get(e);let t=null;try{let{stdout:n}=await mA("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=xA(n)}catch{t=null}return Zl.set(e,t),t}async function ro(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await zt())if((await de(n.path))?.toLowerCase()===t)return n.path;return null}var mA,bh,hA,wA,gA,yA,Zl,hr=O(()=>{mA=pA(fA),bh=process.env.HOME||"/home/globo",hA=_n.join(process.env.BOSTRAT_STATE_DIR||_n.join(bh,".bostrat"),"repos"),wA=[...(process.env.BOSTRAT_REPOS_ROOT||_n.join(bh,"Documents")).split(","),hA].map(e=>e.trim()).filter(Boolean),gA=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,yA=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Zl=new Map});import vA from"node:fs";import kh from"node:fs/promises";import Is from"node:path";import SA from"node:os";import{fileURLToPath as TA}from"node:url";import{execFile as AA}from"node:child_process";import{promisify as EA}from"node:util";function be(e=process.env){let t=e.BOSTRAT_PLANE_URL||"";if(!t)return null;try{let n=new URL(t);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function yi(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function bi(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!vh.test(n))throw oo("repo must be owner/name",400);let r=eu.get(n);if(r&&r.expiresAt-Date.now()>RA)return r;if(!yi(t))throw oo("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Sh(`${be(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Th(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw oo(`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 oo(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 eu.set(n,a),a}async function Rs({env:e=process.env,force:t=!1}={}){if(!yi(e))return{configured:!1,repos:[],username:null};if(!t&&wr&&Date.now()-wr.at<$A)return wr.data;try{let n=await Sh(`${be(e)}/v1/github/repos`,{headers:Th(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return wr={at:Date.now(),data:r},r}catch(n){return wr?wr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Ah(){eu.clear(),wr=null}async function $t({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await de(e):null);if(!r)return{};let{token:o}=await bi(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function tu({env:e=process.env}={}){return(await Rs({env:e})).username??null}function NA(e){let[t,n]=String(e).split("/");return Is.join(IA,t,n)}async function nu(e,{env:t=process.env}={}){let n=(...r)=>xh("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${CA()}" ${MA(t)}`),await n("config","credential.useHttpPath","true")}async function ki(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!vh.test(String(e||"")))throw oo(`bad repo slug "${e}"`,400);let r=NA(e);if(vA.existsSync(Is.join(r,".git")))return r;let{token:o}=await bi(e,{env:t});await kh.mkdir(Is.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await xh("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:OA,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await kh.rm(r,{recursive:!0,force:!0}).catch(()=>{}),oo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await nu(r,{env:t}),r}var xh,_A,IA,vh,RA,$A,OA,Sh,eu,wr,Th,oo,CA,MA,Ge=O(()=>{hr();xh=EA(AA),_A=process.env.BOSTRAT_STATE_DIR||Is.join(process.env.HOME||SA.tmpdir(),".bostrat"),IA=Is.join(_A,"repos"),vh=/^[^/\s]+\/[^/\s]+$/,RA=5*6e4,$A=6e4,OA=10*6e4,Sh=fetch,eu=new Map,wr=null;Th=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),oo=(e,t)=>Object.assign(new Error(e),{status:t});CA=()=>TA(new URL("../bin/bostrat-git-credential",import.meta.url)),MA=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Eh(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ih(e,t,n){let r=be(n),o=new AbortController,s=setTimeout(()=>o.abort(),BA);try{return await DA(`${r}${e}`,{...t,signal:o.signal})}finally{clearTimeout(s)}}async function Rh(e,{env:t=process.env,force:n=!1}={}){if(!Eh(t))return{facts:[],source:"unconfigured"};let r=ru.get(e);if(!n&&r&&Date.now()-r.at<PA)return{facts:r.facts,source:"cache"};try{let o=await Ih(`/v1/memory/${encodeURIComponent(e)}`,{headers:_h(t)},t);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return ru.set(e,{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 xi(e,t,{env:n=process.env}={}){if(!Eh(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Ih(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{..._h(n),"content-type":"application/json"},body:JSON.stringify({facts:t})},n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.results)?o.results:[];return ru.delete(e),{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 PA,BA,ru,DA,_h,$h=O(()=>{Ge();PA=6e4,BA=5e3,ru=new Map,DA=(...e)=>fetch(...e);_h=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import K from"node:fs/promises";import jA from"node:os";import re from"node:path";import{execFile as LA}from"node:child_process";import{promisify as FA}from"node:util";function vi(){return re.join(ou,"memory","repos")}function gr(e){return e===Jt?re.join(ou,"memory",Jt):re.join(vi(),e)}async function $s(e){try{return JSON.parse(await K.readFile(re.join(e,Ph),"utf8"))}catch{return{}}}async function su(e,t){let n=await $s(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await K.mkdir(e,{recursive:!0}),await K.writeFile(re.join(e,Ph),JSON.stringify(n))}function Ch(e){let t=String(e||"").trim();if(!t)return null;let r=(t.replace(/\/+$/,"").split(/[/:]/).pop()||"").replace(/\.git$/i,"").toLowerCase();return/^[a-z0-9][a-z0-9._-]*$/.test(r)?r:null}async function Mh(e){try{let{stdout:t}=await UA("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function au(e){if(!e)return[];let t=Ch(await Mh(e));if(t)return[t];let n=[],r=[];try{r=await K.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Ch(await Mh(re.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Si(e){let t=/^---\n([\s\S]*?)\n---\n?/.exec(e);return t?{fm:t[1],body:e.slice(t[0].length)}:{fm:null,body:e}}function In(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function Ot(e,t=null){let n=t??await K.readFile(e,"utf8").catch(()=>""),{fm:r}=Si(n),o=In(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await K.stat(e)).mtimeMs}catch{return 0}}function Bh(e,t){let n=In(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Jt){let o=In(e,"trust");if(!o||/^operator/i.test(o))return Jt}return t}function iu(e,t,n,r=new Map){let o=(e||"").split(`
|
|
15
|
+
`),s=new Map;for(let c of(t||"").split(`
|
|
16
|
+
`)){let l=so(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=so(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 K.mkdir(n,{recursive:!0}),await K.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function
|
|
23
|
-
`);
|
|
24
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
25
|
-
`);return n}async function
|
|
19
|
+
`)}function Dh(e,t){let n=In(t,"name")||e.replace(/\.md$/,""),r=In(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Os(e){let t=[];try{t=await K.readdir(e,{withFileTypes:!0})}catch{return[]}return t.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function jh(e,t){let n=re.join(e,".bostrat","memory"),r=await Os(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=re.join(n,l),f;try{f=await K.readFile(u,"utf8")}catch{continue}let{fm:d}=Si(f),m=Bh(d,t);if(!m)continue;let h=gr(m),w=re.join(h,l),y=await Ot(u,f);if(i.has(h)||i.set(h,await $s(h)),(i.get(h)[l]||0)>=y)continue;let b=await Ot(w).catch(()=>0);if(await K.access(w).then(()=>!0).catch(()=>!1)&&b>=y)continue;await K.mkdir(h,{recursive:!0}),await K.writeFile(w,f),a++;let E=o.get(h);E||o.set(h,E={won:new Set,generated:new Map}),E.won.add(l),E.generated.set(l,Dh(l,d)),s.has(m)||s.set(m,new Set),s.get(m).add(l)}let c=await K.readFile(re.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=re.join(l,"MEMORY.md"),d=await K.readFile(f,"utf8").catch(()=>"");await K.writeFile(f,iu(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function WA(){let e=re.join(ou,"workspaces"),t=[];try{t=await K.readdir(e,{withFileTypes:!0})}catch{return{harvested:0}}let n=0,r=new Map;for(let o of t){if(!o.isDirectory())continue;let s=re.join(e,o.name);if(!await K.access(re.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await au(s),c=await jh(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 GA(e,t){let n=gr(e),r=await $s(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!qA.test(l)||l==="MEMORY.md")continue;let u=re.join(n,l),f=await K.access(u).then(()=>!0).catch(()=>!1),d=f?await Ot(u):0,m=Number(c.modified)||0;if(c.deleted){m>d&&(f&&await K.rm(u,{force:!0}),await su(n,[[l,m]]),f&&(a.push(l),i++));continue}f&&d>=m||(r[l]||0)>=m||(await K.mkdir(n,{recursive:!0}),await K.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=re.join(n,"MEMORY.md"),l=await K.readFile(c,"utf8").catch(()=>""),u=iu(l,[...s.values()].join(`
|
|
20
|
+
`),o);if(a.length){let f=new Set(a);u=u.split(`
|
|
21
|
+
`).filter(d=>!f.has(so(d))).join(`
|
|
22
|
+
`)}await K.mkdir(n,{recursive:!0}),await K.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function cu(e,t=null){let n=gr(e),r=t?.length?t:await Os(n);if(!r.length)return{pushed:0,source:"empty"};let o=KA(await K.readFile(re.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=re.join(n,a),c;try{c=await K.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await Ot(i,c),indexLine:o.get(a)??null})}return xi(e,s)}async function VA(e){let{facts:t,source:n}=await Rh(e);t.length?await GA(e,t):n==="plane"&&await cu(e)}function zA(e){let t=e.split(`
|
|
23
|
+
`);t.length>Oh&&(t=t.slice(0,Oh));let n=t.join(`
|
|
24
|
+
`);for(;Buffer.byteLength(n,"utf8")>HA&&t.length;)t.pop(),n=t.join(`
|
|
25
|
+
`);return n}async function YA(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=re.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let f=gr(u),d=await $s(f);for(let[y,b]of Object.entries(d)){let x=re.join(n,y);await K.access(x).then(()=>!0).catch(()=>!1)&&await Ot(x)<=b&&(await K.rm(x,{force:!0}),o++)}let m=await Os(f);if(!m.length)continue;await K.mkdir(n,{recursive:!0});for(let y of m){let b=re.join(f,y),x=re.join(n,y),E=await Ot(b),_=await Ot(x).catch(()=>0);await K.access(x).then(()=>!0).catch(()=>!1)&&_>=E||(await K.copyFile(b,x),r++)}let h=await K.readFile(re.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===Jt?"## operator memory":`## repo: ${u}`;h.trim()&&s.push(t.length>1?`${w}
|
|
26
26
|
|
|
27
27
|
${h.trim()}`:h.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
|
|
28
28
|
|
|
29
29
|
`)+`
|
|
30
|
-
`,c=
|
|
31
|
-
`).filter(
|
|
32
|
-
`);await K.writeFile(c,u).catch(()=>{});let
|
|
33
|
-
`).filter(l=>
|
|
34
|
-
`)).catch(()=>{});let c=await
|
|
35
|
-
`)){let r=
|
|
30
|
+
`,c=zA(i);await K.mkdir(n,{recursive:!0}),await K.writeFile(re.join(n,"MEMORY.md"),c);let l=re.join(n,"claude");return await K.mkdir(l,{recursive:!0}),await K.writeFile(re.join(l,"MEMORY.md"),JA,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Lh(e){try{let t=await au(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,Jt];if(Date.now()-Nh>XA){Nh=Date.now(),await ZA();let{changedByKey:r}=await WA();for(let[o,s]of r)await cu(o,[...s])}for(let r of n)await VA(r);return await YA(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}async function ZA(){let e=0,t=[];try{t=await K.readdir(vi(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=re.join(vi(),n.name),o=gr(Jt),s=new Set,a=new Map;for(let m of await Os(r)){let h=re.join(r,m),w;try{w=await K.readFile(h,"utf8")}catch{continue}let{fm:y}=Si(w);if(Bh(y,n.name)!==Jt)continue;let b=re.join(o,m),x=await Ot(b).catch(()=>0),E=await Ot(h,w);await K.mkdir(o,{recursive:!0}),E>x&&await K.writeFile(b,w),await K.rm(h,{force:!0}),s.add(m),a.set(m,Dh(m,y)),e++}if(!s.size)continue;let i=Date.now();await su(r,[...s].map(m=>[m,i])),await xi(n.name,[...s].map(m=>({file:m,deleted:!0,modified:i})));let c=re.join(r,"MEMORY.md"),l=await K.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
31
|
+
`).filter(m=>!s.has(so(m))).join(`
|
|
32
|
+
`);await K.writeFile(c,u).catch(()=>{});let f=re.join(o,"MEMORY.md"),d=await K.readFile(f,"utf8").catch(()=>"");await K.writeFile(f,iu(d,l,s,a))}return{moved:e}}async function Fh(){let e=[],t=[Jt];try{for(let n of await K.readdir(vi(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=gr(n),o=await Os(r),s=await $s(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await K.readFile(re.join(r,i),"utf8").catch(()=>""),{fm:l}=Si(c);a.push({file:i,name:In(l,"name")||i.replace(/\.md$/,""),description:In(l,"description")||"",scope:In(l,"scope")||null,modified:await Ot(re.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function Uh(e,t){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(t)||t==="MEMORY.md")throw new Error("bad fact filename");let n=gr(e),r=re.join(n,t);if(!await K.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await K.rm(r,{force:!0}),await su(n,[[t,s]]);let a=re.join(n,"MEMORY.md"),i=await K.readFile(a,"utf8").catch(()=>"");await K.writeFile(a,i.split(`
|
|
33
|
+
`).filter(l=>so(l)!==t).join(`
|
|
34
|
+
`)).catch(()=>{});let c=await xi(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Hh(e){try{let t=await au(e),{harvested:n,changedByKey:r}=await jh(e,t[0]||null);for(let[o,s]of r)await cu(o,[...s]);return{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}var UA,ou,Oh,HA,Jt,Ph,so,qA,KA,JA,Nh,XA,Ti=O(()=>{$h();UA=FA(LA),ou=process.env.BOSTRAT_STATE_DIR||re.join(process.env.HOME||jA.tmpdir(),".bostrat"),Oh=200,HA=25*1024;Jt="operator";Ph=".tombstones.json";so=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;qA=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,KA=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
35
|
+
`)){let r=so(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};JA=`# 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
|
+
`;Nh=0,XA=3e4});import lu from"node:fs/promises";import QA from"node:path";async function Qe(e,t,{mode:n}={}){await lu.mkdir(QA.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++eE}.tmp`;await lu.writeFile(r,JSON.stringify(t,null,2)+`
|
|
41
|
+
`,n!=null?{mode:n}:void 0),await lu.rename(r,e)}var eE,Rn=O(()=>{eE=0});import Vh from"node:fs/promises";import zh from"node:os";import Jh from"node:path";function Te(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ns(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),tE);try{return await iE(`${be(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function Xh(){return Ei||(uu||=Vh.readFile(Yh(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>Ei={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>uu=null),uu)}async function _i(){await Qe(Yh(),Ei).catch(()=>{})}function Kh(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function fu(e){let t=await Xh(),n=Kh(e);if(e.op==="delete")t.ops=t.ops.filter(r=>!(r.family===e.family&&r.id===e.id)),t.ops.push(e);else{t.ops=t.ops.filter(o=>!(o.op==="delete"&&o.family===e.family&&o.id===e.id));let r=t.ops.findIndex(o=>Kh(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>Wh&&(t.ops=t.ops.slice(-Wh)),await _i()}function Gh(){Ai||(Ai=setTimeout(()=>{Ai=null,ao().catch(()=>{})},cE),Ai.unref?.())}function du(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function uE(e,t=process.env){if(e.op==="record"){let n=await Ns(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Ms(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:aE()}]})},t);if(!n.ok)throw du(n.status);let o=(await n.json().catch(()=>null))?.results?.[0];return o?.outcome==="rejected"?{delivered:!1,rejected:o.reason}:{delivered:!0}}if(e.op==="blob"){let n=await Vh.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Ns(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Ms(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw du(r.status);return r.status===400?{delivered:!1,rejected:(await r.json().catch(()=>null))?.error||"rejected"}:{delivered:!0}}if(e.op==="delete"){let n=await Ns(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Ms(t)},t);if(!n.ok)throw du(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function ao({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Te(e)?Cs||(Cs=(async()=>{let r=await Xh();dE(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await uE(a,e)}catch(c){if(!c?.refused)return Gh(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<rE)return Gh(),await _i(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};t.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>qh&&(r.parked=r.parked.slice(-qh)),s++,await _i();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await _i()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Cs=null}),Cs):{flushed:0,source:"unconfigured"}}function dE(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=lE);if(n.length){e.parked=e.parked.filter(r=>!n.includes(r));for(let r of n)e.ops.push({...r,refusals:0,parkedAt:void 0,error:void 0})}}async function Ve(e,t,{trackId:n=null,env:r=process.env}={}){return Te(r)?(await fu({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),ao({env:r})):{queued:!1,source:"unconfigured"}}async function $n(e,t,n,r,o,{env:s=process.env}={}){return Te(s)?(await fu({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),ao({env:s})):{queued:!1,source:"unconfigured"}}async function kt(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Te(r)?(await fu({op:"delete",family:e,id:t,deletedAt:n}),ao({env:r})):{queued:!1,source:"unconfigured"}}async function Be(e,{env:t=process.env}={}){if(!Te(t))return{records:[],source:"unconfigured"};try{let n=await Ns(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Ms(t)},t);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 Ps(e,t,n,r,{env:o=process.env}={}){if(!Te(o))return null;try{let s=await Ns(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ms(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 tE,nE,Wh,rE,qh,oE,sE,Yh,aE,iE,cE,lE,Ms,Ei,uu,Ai,Cs,On=O(()=>{Ge();Rn();tE=1e4,nE=3e4,Wh=2e3,rE=5,qh=100,oE=60*6e4,sE=()=>process.env.BOSTRAT_STATE_DIR||Jh.join(process.env.HOME||zh.tmpdir(),".bostrat"),Yh=()=>Jh.join(sE(),"content-outbox.json"),aE=()=>process.env.BOSTRAT_NODE_ID||zh.hostname(),iE=(...e)=>fetch(...e),cE=nE,lE=oE;Ms=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});Ei=null,uu=null,Ai=null,Cs=null});import pu from"node:fs/promises";import Zh from"node:path";async function Qh(e,t,{box:n=null,agent:r=null}={}){if(!e||!Te())return{pushed:!1};try{let o=Date.now();return await Ve("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await $n("checkpoints",e,o,"md",t),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function Ii(e){try{let t=mE(e);if(!t||!Te())return{restored:!1};let n=Zh.join(e,fE,pE);if(await pu.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await Be("checkpoints");if(o!=="plane")return{restored:!1};let s=r.find(i=>!i.deletedAt&&i.data?.id===t);if(!s?.data?.v)return{restored:!1};let a=await Ps("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await pu.mkdir(Zh.dirname(n),{recursive:!0}),await pu.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var fE,pE,mE,Ri=O(()=>{On();fE=".bostrat/handoff",pE="latest.md",mE=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null});import Bs from"node:fs/promises";import hE from"node:os";import tw from"node:path";function yr(e){let t=String(e??"");for(let n of wE)t=t.replace(n,"\u2039redacted\u203A");return t}function $i(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function gE(e){try{await Bs.mkdir(mu,{recursive:!0}),await Bs.appendFile(io,`${JSON.stringify(e)}
|
|
42
|
+
`,"utf8");let n=(await Bs.readFile(io,"utf8")).split(`
|
|
43
|
+
`).filter(Boolean);n.length>ew&&await Bs.writeFile(io,`${n.slice(-ew).join(`
|
|
44
44
|
`)}
|
|
45
|
-
`,"utf8")}catch(
|
|
46
|
-
`).filter(Boolean).slice(-
|
|
47
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,
|
|
48
|
-
`).map(n=>n.trim()).includes(
|
|
49
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
50
|
-
`);for(let n=
|
|
51
|
-
`){
|
|
52
|
-
`,n);i!==-1&&(
|
|
53
|
-
`).map(n=>n.trim()).find(Boolean)||
|
|
54
|
-
`)){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(
|
|
55
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=
|
|
56
|
-
`)){let
|
|
57
|
-
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(
|
|
58
|
-
`)){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
|
|
59
|
-
`).trim():"";
|
|
60
|
-
`).trim():""}function
|
|
61
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.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 w of l.content){let y=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof y=="string"&&
|
|
62
|
-
|
|
63
|
-
`):""}var
|
|
64
|
-
`);if(!
|
|
65
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
66
|
-
`);return
|
|
67
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
68
|
-
`).pop()||"";return
|
|
69
|
-
`),o=r.findIndex(i=>
|
|
70
|
-
`).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:
|
|
71
|
-
`),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:
|
|
72
|
-
`))
|
|
73
|
-
`).slice(-
|
|
74
|
-
`);return F_.test(n)}function Ls(t,e){return Pu(t,e)==="busy"}function Bi(t,e){return Pu(t,e)!=="idle"}function Di(t,e){let n=Bt[t];return n?.up?n.up.test(e):!0}function H_(t,e){let n=Bt[t];return n?Bu(t,e)?!1:n.up.test(e)&&Pu(t,e)==="idle":!0}function Bu(t,e){let n=Bt[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 Fw(t,e,n,{bailOnDialog:r=!1}={}){let o=Date.now()+n,s=0;for(await pr(t).catch(()=>{});;){let a=await xt(t);if(H_(e,a)){await $n(js);return}if(r&&e==="claude"&&M_.test(a)&&!Bt.claude.up.test(a)){z({flow:"agent-ready",phase:"dialog",sessionName:t,agent:e,dialog:"resume-confirm"}).catch(()=>{});return}if(e==="cursor"&&C_.test(a)&&!Bt.cursor.up.test(a)&&s<3){if(!Lw(t,"cursor-trust")){await $n(Mu);continue}s+=1,await z({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:s}).catch(()=>{}),await Pt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await $n(js);continue}if(e==="codex"&&s<3){let i=P_(a);if(i){if(!Lw(t,`codex-update:${i.from}->${i.to}`)){await $n(Mu);continue}s+=1,await z({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:s,update:`${i.from} -> ${i.to}`}).catch(()=>{}),await Pt(t,"3",{enter:!1,kind:"codex-update-skip"}),Cw({sessionName:t,from:i.from,to:i.to}),await $n(js);continue}}if(Date.now()>o){if(Bu(e,a)){let i=`${e} is blocked on an interactive prompt`;throw e==="codex"?i=Bt.codex.auth.test(a)?"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"&&Bt.cursor.auth.test(a)?i="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"&&Bt.claude.auth.test(a)?i="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"&&Bt.claude.block.test(a)&&(i='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'),z({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}if(await ao(t)){let i=`${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 z({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:t,agent:e,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),z({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:s,paneBlocked:!!Bt[e]?.block?.test(a),paneUp:!!Bt[e]?.up?.test(a),paneBusy:!!Bt[e]?.busy?.test(a)}).catch(()=>{}),await $n(js);return}await $n(Mu)}}function Hw({agent:t,agentCmd:e}={}){let n=e?En(e):En(t||"");return{label:n,realClaude:e?fr(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 Fs({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Hw({agent:n,agentCmd:r});return!e||!o&&!s?null:On({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function On({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=O_(),bailOnDialog:a=!1}={}){let{label:i,realClaude:c,realCodex:l,realCursor:u}=Hw({agent:r,agentCmd:o});if(c&&t&&await kn(t),l&&t&&await Yr(t),u&&t&&await Xr(t),(c||l||u)&&n){await Fw(n,i,s,{bailOnDialog:a});return}let d=Bt[r]?r:Bt[i]?i:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&d){await Fw(n,d,s);return}await $n(js)}var O_,Mu,js,Bt,C_,M_,N_,B_,Pi,D_,j_,F_,U_,ji=C(()=>{Gt();bs();ci();li();Ou();Ni();Ge();O_=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Mu=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,js=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Bt={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/}},C_=/Trust this workspace|\[a\]\s*Trust/,M_=/Resume from summary/i,N_=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;B_=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Pi=new Map;D_=68,j_=/^\s*[─━]{10,}\s*$/;F_=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,U_=15});function Oe(t){return t==="codex"?{enter:!0,submitDelayMs:W_}:t==="claude"?{enter:!0,submitDelayMs:q_}:{enter:!0}}var W_,q_,Us=C(()=>{W_=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),q_=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})()});function yr(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of K_){let o=0;for(let s of G_[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Ww(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))V_.has(r)&&(e+=o||0),z_.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Hs(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 K_,G_,V_,z_,Du=C(()=>{K_=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],G_={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/]};V_=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),z_=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function Vw(t){let e=Hs(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||J_.has(r)||n.push(r);return n}function Y_(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=yr(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 Ws(t){let e=Y_(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=Vw(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 Li(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(Vw(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[h,w]of d){let y=t.lengths[h],k=w+qw*(1-Kw+Kw*(y/(t.avgdl||1))),x=p*(w*(qw+1)/k);c.set(h,(c.get(h)||0)+x)}}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 qw,Kw,J_,ju=C(()=>{Du();qw=1.5,Kw=.75,J_=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 Fi from"node:fs/promises";import zw from"node:path";async function X_(t){let e=zw.join(t,".bostrat","events"),n;try{n=await Fi.readdir(e)}catch{return Ws([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Fi.readFile(zw.join(e,o),"utf8");for(let a of s.split(`
|
|
75
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
76
|
-
${o}`.trim()||
|
|
77
|
-
`):""}async function
|
|
45
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${io}: ${t?.message||t}`)}}async function z(e={}){let t={at:new Date().toISOString(),ts:Date.now(),...e},n=[t.flow||"handoff",t.phase||t.step||"?",t.trackId?`track=${t.trackId}`:"",t.sessionName?`box=${t.sessionName}`:"",t.error?`ERR=${t.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),t.sessionName&&$i())try{t.paneTail=yr((await Ae(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}($i()||t.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(t.phase||t.step||"")))&&await gE(t)}async function nw({limit:e=80}={}){try{return(await Bs.readFile(io,"utf8")).split(`
|
|
46
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=yr(o.paneTail)),o}catch{return{raw:yr(r)}}})}catch{return[]}}var wE,mu,io,ew,rw,Yt=O(()=>{ze();wE=[/[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];mu=process.env.BOSTRAT_DEBUG_DIR||tw.join(hE.homedir(),".bostrat","debug"),io=tw.join(mu,"handoff.jsonl"),ew=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;rw={DEBUG_DIR:mu,LOG_FILE:io}});import{execFile as yE}from"node:child_process";import{promisify as bE}from"node:util";import Ci from"node:fs";import{randomUUID as kE}from"node:crypto";function uo(e){return new Promise(t=>setTimeout(t,e))}function Oi(e,t){if(!t)return e;let n=o=>typeof o=="string"?o.split(t).join("<redacted>"):o,r=new Error(n(e?.message)||"command failed");return e?.code!==void 0&&(r.code=e.code),e?.stderr!==void 0&&(r.stderr=n(e.stderr)),r}function sw(e){return`/tmp/tmux-${e}/default`}function xE({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=sw(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function gu(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${SE}=1`]}function bu(e,t,n){let r=br(e),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(e);if(!r||o)return e;let s=`${e} --session-id ${t}`,a=/(^|\s)(--name|-n)(\s|=)/.test(e);return n&&!a&&Ee(n)&&(s+=` --name ${n}`),s}function br(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Cn(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function AE(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:br(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function aw(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await iw(e,{loginShell:t});return r===Mi?!0:r===co?!1:n}async function iw(e,{loginShell:t=!1,timeoutMs:n=hu}={}){if(!e)return co;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await he(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),Mi}catch(s){return s?.killed||s?.signal?lo:s?.code===1||s?.code===127?co:lo}}async function Ni(e,{retries:t=EE,sleep:n=_E,probe:r=iw}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:hu});for(let a=0;s===lo&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:hu*2});return s}function U(...e){return["-S",st,...e]}async function Mn(){if(!Ci.existsSync(st))return!1;try{return await he(L,U("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Ds(){if(await Mn())return!0;try{Ci.mkdirSync(st.replace(/\/[^/]+$/,""),{recursive:!0}),await he(L,U("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Mn()}function Ee(e){return typeof e=="string"&&IE.test(e)}async function xt(){if(!Ci.existsSync(st))return[];try{let e="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}|#{alternate_on}",{stdout:t}=await he(L,U("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
47
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,m]=r.split("|"),h=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(h)?n-h*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:m==="1"}}).filter(r=>!TE.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function oe(e){return(await xt()).find(n=>n.name===e)||null}function OE(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=$E&&RE.test(t)}function Pi(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function cw(e){try{let{stdout:t}=await he(L,U("list-sessions","-F","#{session_name}"));return t.split(`
|
|
48
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function PE(e){let t=String(e||"").trim().split(/\s+/)[0]||"";if(t.includes("/"))return null;let n=[];for(let o=1;o<=t.length-3;o++)n.push(t.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function BE(e,t){let n=Date.now()+CE(),r=PE(t),o=!1,s=0;for(;;){if(await uo(ME),s+=1,!await cw(e))return;let a=await Ae(e,{lines:15}),i=!o&&NE.test(a)&&!!r?.test(a);if(!i&&s>1&&!await fo(e))return;if(i){o=!0,await z({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Cn(t),promptHead:t.slice(0,120)}).catch(()=>{}),await he(L,U("send-keys","-t",e,"C-u")),await he(L,U("send-keys","-t",e,"-l",t)),await he(L,U("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await z({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Cn(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Nn(e,t=vE,n={}){if(!OE(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=Pi(r);if(!Ee(o))throw new Error("resulting tmux session name is invalid");if(!await Ds())throw new Error(`host tmux server not running on ${st} and auto-start failed \u2014 start one: ${L} -S ${st} new-session -d -s keeper "sleep 2147483647"`);if(await cw(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||wu,a=n.agent||Cn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||kE(),l=i?s:bu(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&br(s),d=n.firstRun===!0;d||(f&&await En(t),a==="codex"&&await to(t),a==="cursor"&&await no(t),await Lh(t),await Ii(t));let m=f&&!d?mi():null,h=ph(),w=["-e",`${fi}=${h}`];m&&w.push("-e",`ANTHROPIC_API_KEY=${m}`),f&&w.push(...gu());try{await he(L,U("new-session","-d","-s",o,"-c",t,...w))}catch(y){throw Oi(m?Oi(y,m):y,h)}return await kr(o),await he(L,U("set-option","-t",o,"@bostrat-session","1")),await he(L,U("set-option","-t",o,"@bostrat-title",r)),await he(L,U("set-option","-t",o,"@bostrat-sid",c)),await he(L,U("set-option","-t",o,"@bostrat-agent",a)),await he(L,U("send-keys","-t",o,l,"Enter")),AE(l)&&BE(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:h}}async function Ct(e){if(!Ee(e))throw new Error("invalid session name");await he(L,U("kill-session","-t",e))}async function lw(e,t){if(Ee(e))try{let n=t?U("set-option","-t",e,"@bostrat-sid",t):U("set-option","-u","-t",e,"@bostrat-sid");await he(L,n)}catch{}}async function Ae(e,{lines:t=40}={}){if(!Ee(e))return"";try{let n=U("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await he(L,n);return String(r||"")}catch{return""}}async function uw(e){if(!Ee(e))return 0;try{let{stdout:t}=await he(L,U("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
49
|
+
`).filter(Boolean).length}catch{return 0}}async function kr(e,{cols:t=LE,rows:n=FE}={}){if(!Ee(e))return!0;if(await uw(e))return!1;try{let r=await dw(e);if(r.cols===t&&r.rows===n)return!0;await he(L,U("resize-window","-t",e,"-x",String(t),"-y",String(n))),await he(L,U("set-window-option","-t",e,"window-size","latest")),await uo(80)}catch{}return!0}async function UE(e){if(!Ee(e))return 0;try{let{stdout:t}=await he(L,U("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function fo(e){let t=await UE(e);if(!t)return!1;try{return await he("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function dw(e){if(!Ee(e))return{cols:0,rows:0};try{let{stdout:t}=await he(L,U("display-message","-t",e,"-p","#{window_width} #{window_height}")),[n,r]=String(t||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function Bi(e,{cols:t=DE,rows:n=jE}={}){if(Ee(e)&&!await uw(e))try{let r=await dw(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await he(L,U("resize-window","-t",e,"-x",String(o),"-y",String(s))),await he(L,U("set-window-option","-t",e,"window-size","latest")),await uo(80))}catch{}}async function Xt(e){return await Bi(e),Ae(e,{lines:De})}async function Oe(e,t,{settleMs:n=40,kind:r}={}){if(!Ee(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>HE.has(s));for(let s=0;s<o.length;s++)await he(L,U("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await uo(n);return z({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function po(e,t,{kind:n}={}){if(!Ee(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await he(L,U("send-keys","-t",e,"-N",String(r),"BSpace")),z({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function ji(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!Ee(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await he(L,U("send-keys","-t",e,"-l",s)),n>0&&await uo(n);z({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}async function je(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!Ee(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&await he(L,U("send-keys","-t",e,"-l",t)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await uo(s),await he(L,U("send-keys","-t",e,"Enter"))}z({flow:"tmux",phase:"send",sessionName:e,kind:o||"text",textLen:typeof t=="string"?t.length:0,textHead:typeof t=="string"?t.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}var he,L,ow,st,vE,wu,SE,yu,TE,Mi,co,lo,hu,EE,_E,ku,IE,RE,$E,CE,ME,NE,DE,jE,De,LE,FE,HE,Di,ze=O(()=>{Es();wi();gi();Ti();Ri();Yt();As();he=bE(yE);L=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";ow=typeof process.getuid=="function"?process.getuid():0,st=xE({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return Ci.existsSync(sw(ow))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:ow}),vE=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",wu=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",SE="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";yu="bostrat-usage-probe",TE=new Set([yu]);Mi="present",co="absent",lo="unknown",hu=5e3,EE=[250,1e3,3e3],_E=e=>new Promise(t=>setTimeout(t,e));ku={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"};IE=/^[A-Za-z0-9_.-]{1,64}$/;RE=/^[A-Za-z0-9 ._↻-]+$/,$E=64;CE=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,ME=1e3,NE=/not found|Unknown command/i;DE=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,jE=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,De=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,LE=Number(process.env.BOSTRAT_BOX_COLS)||120,FE=Number(process.env.BOSTRAT_BOX_ROWS)||40;HE=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);Di=1600});import js from"node:fs";import zE from"node:fs/promises";import mo from"node:path";import JE from"node:os";function Su(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function Tu(e){let t=mo.join(vu,Su(e)),n;try{n=js.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=mo.join(t,o);try{let{mtimeMs:a}=js.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Zt(e,t){if(!t)return null;let n=mo.join(vu,Su(e),`${t}.jsonl`);return js.existsSync(n)?n:null}function yw(e){if(!e)return[];let t=mo.join(vu,Su(e)),n;try{n=js.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=js.statSync(mo.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function et(e,t=131072){let n=await zE.open(e,"r");try{let{size:r}=await n.stat(),o=Math.max(0,r-t),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 XE(e){let t=e.split(`
|
|
50
|
+
`);for(let n=t.length-1;n>=0;n--){let r=t[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 Ls(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=Zt(e,t),o=!0,!r)return null}else if(n){if(r=Tu(e),o=!1,!r)return null}else return null;let s;try{s=XE(await et(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(gw,s.model),i=gw[s.model]||YE;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 gw,YE,vu,vt=O(()=>{gw={"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},YE=2e5,vu=process.env.BOSTRAT_CLAUDE_PROJECTS||mo.join(JE.homedir(),".claude","projects")});function ZE(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o];if(r){n+=s,s===r&&e[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&e[o+1]==="&"){t.push(n),n="",o++;continue}if(s==="|"&&e[o+1]==="|"){t.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
|
|
51
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function Fs(e){let t=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}t.push(n),n="",o=!1}};for(let a=0;a<e.length;a++){let i=e[a];if(r){i===r&&e[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&e[a+1]==="<")return s(),t.push("<<"),t;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),t}function Us(e){let t=e;for(;t.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[0]);)t=t.slice(1);for(;t.length&&(t[0]==="sudo"||t[0]==="time"||t[0]==="env");)t=t.slice(1);return t}function ke(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(xw(o)){t.has(o)&&r++;continue}n.push(o)}return n}function vw(e){let t=e.indexOf("-n"),n=t!==-1?e[t+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 t_(e){let t=vw(e),n=He(ke(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${_e(n)}`:`Read lines ${t.from}\u2013${t.to} of ${_e(n)}`:e.includes("-i")&&n?`Edit ${_e(n)}`:null}function Au(e){if(e.includes("--files")){let s=He(ke(e,bw)[0]);return s&&s!=="."?`List files in ${ho(s)}`:"List files"}let t=ke(e,bw),n=t[0];if(!n)return null;let r=He(t.slice(1).find(s=>s!==".")),o=wo(n,40);return r?`Search for \u201C${o}\u201D in ${ho(r)}`:`Search for \u201C${o}\u201D`}function Eu(e){let t=ke(e,new Set(["--prefix"])),n=t[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?t[1]?`Run ${t[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function _u(e,t){if(e.includes("-e")||e.includes("--eval")||e.includes("-c")||e[1]==="-"||e.includes("<<"))return`Run an inline ${t} script`;if(e.includes("--test"))return"Run tests";let n=ke(e)[0];return n?`Run ${_e(n)}`:null}function n_(e){let t=ke(e),n=t[0],r=t.find(a=>/\.(service|timer|socket|target)$/.test(a))||t[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 kw(e){return e.find(t=>/^https?:\/\//.test(t))||null}function s_(e){let t=Us(Fs(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(_e(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return Iu(t[2]);let n=_e(t[0]);if(o_.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=r_[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function Iu(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
52
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=ZE(t);for(let i=0;i<r.length-1;i++){let c=Us(Fs(r[i])),l=Us(Fs(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(_e(c[0]))&&_e(l[0])==="sed"){let u=vw(l),f=ke(c)[0];if(u&&f)return wo(u.from===u.to?`Read line ${u.from} of ${_e(f)}`:`Read lines ${u.from}\u2013${u.to} of ${_e(f)}`,88)}}let o=[];for(let i of r){let c=s_(i);c&&o.push({d:c,head:_e(Us(Fs(i))[0]||""),sub:ke(Us(Fs(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":wo(s?s.d:o[0].d,88)}var wo,He,_e,ho,xw,bw,QE,e_,r_,o_,Sw=O(()=>{wo=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,He=e=>e&&!/[$`]/.test(e)?e:null,_e=e=>e.replace(/\/+$/,"").split("/").pop()||e,ho=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},xw=e=>e.startsWith("-"),bw=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);QE={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:e=>e[1]?`Clone ${_e(e[1])}`:"Clone repository",checkout:()=>"Switch branches",switch:()=>"Switch branches",restore:()=>"Restore files",reset:()=>"Reset git state",branch:(e,t)=>t.includes("--show-current")?"Check current branch":"List branches",remote:()=>"Check git remotes","rev-parse":()=>"Resolve a git ref",worktree:()=>"Manage git worktrees","merge-base":()=>"Find the merge base","ls-files":()=>"List tracked files"},e_={pr:e=>({create:"Create a pull request",list:"List pull requests",view:"View a pull request",merge:"Merge a pull request",checks:"Check PR status"})[e[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"};r_={git:e=>{let t=ke(e,new Set(["-C","-c"])),n=QE[t[0]];return n?n(t,e):null},gh:e=>{let t=ke(e),n=e_[t[0]];return n?n(t):null},sed:t_,rg:Au,grep:Au,egrep:Au,nl:e=>{let t=He(ke(e)[0]);return t?`Read ${_e(t)}`:null},cat:e=>{let t=He(ke(e)[0]);return t?`Read ${_e(t)}`:null},head:e=>{let t=He(ke(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${_e(t)}`:null},tail:e=>{let t=He(ke(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${_e(t)}`:null},wc:e=>{let t=He(ke(e)[0]);return t?`Count lines in ${_e(t)}`:"Count input lines"},ls:e=>{let t=He(ke(e)[0]);return t&&t!=="."?`List files in ${ho(t)}`:"List files"},find:e=>He(e[1])&&!xw(e[1])&&e[1]!=="."?`Find files in ${ho(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=He(ke(e)[0]);return t?`Create directory ${ho(t)}`:null},touch:e=>{let t=He(ke(e)[0]);return t?`Create ${_e(t)}`:null},rm:e=>{let t=He(ke(e)[0]);return t?`Delete ${ho(t)}`:null},mv:e=>{let t=He(ke(e)[0]);return t?`Move ${_e(t)}`:null},cp:e=>{let t=He(ke(e)[0]);return t?`Copy ${_e(t)}`:null},chmod:e=>{let t=He(ke(e)[1]);return t?`Change permissions on ${_e(t)}`:null},diff:e=>{let t=ke(e);return t.length>=2?`Compare ${_e(t[0])} and ${_e(t[1])}`:null},curl:e=>{let t=kw(e);return t?`Fetch ${wo(t,56)}`:"Fetch a URL"},wget:e=>{let t=kw(e);return t?`Fetch ${wo(t,56)}`:"Fetch a URL"},npm:Eu,pnpm:Eu,yarn:Eu,npx:e=>{let t=ke(e)[0];return t?`Run ${t}`:null},node:e=>_u(e,"Node"),python:e=>_u(e,"Python"),python3:e=>_u(e,"Python"),make:e=>{let t=ke(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:n_,journalctl:e=>{let t=e.findIndex(r=>r==="-u"||r==="--unit"||r==="--user-unit"),n=t!==-1?e[t+1]:null;return n?`Read ${n.replace(/\.service$/,"")} logs`:"Read system logs"},tmux:e=>{let t=ke(e,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"}[t]||null},ps:()=>"List processes",which:e=>{let t=ke(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=ke(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},o_=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function go(e){let t=e.split(`
|
|
53
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Pn(e,t=""){let n=t.trim();return n||Iu(e)||go(e)}function Ru(e,t){if(e){if(Array.isArray(e)){for(let n of e)Ru(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&$u.has(e.name)){let n=String(e.input?.command||"").trim();n&&t.push({id:e.id||null,tool:e.name,command:n,description:String(e.input?.description||"").trim()})}for(let n of Object.values(e))Ru(n,t)}}}async function Tw(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await et(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
54
|
+
`)){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(Ru(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:Pn(f.command,f.description),ts:u})}return o.slice(-n)}function Aw(e,t){let n=t.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((h,w)=>w>=s&&(h.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let m=d.map(h=>({...h,n:i(h)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:m,ts:Math.max(...m.map(h=>h.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var $u,yo=O(()=>{vt();Sw();$u=new Set(["Bash"])});import Ew from"node:fs";import Cu from"node:fs/promises";import Ou from"node:path";import a_ from"node:os";async function _w(e){try{return await Cu.realpath(e)}catch{return e}}function c_(e=i_){let t=[],n=r=>{let o;try{o=Ew.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Ou.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{t.push({id:i?i[1]:null,file:a,mtimeMs:Ew.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function l_(e,t=1048576){let n;try{n=await Cu.open(e,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(t,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(`
|
|
55
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function u_(e){let t=await l_(e);if(!t)return null;let n;try{n=JSON.parse(t)}catch{return null}return n?.type==="session_meta"&&n?.payload?.cwd||null}async function xr(e,t){if(!e&&!t)return null;let n=c_();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await _w(e);for(let o of n){let s=await u_(o.file);if(s&&(s===e||await _w(s)===r))return o}return null}async function Iw(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await xr(e,t);if(!o)return[];let s;try{s=await et(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
|
|
56
|
+
`)){let m=d.trim();if(!m||m[0]!=="{")continue;let h;try{h=JSON.parse(m)}catch{continue}if((h.type==="turn_context"&&h.payload?.model||h.type==="session_meta"&&h.payload?.model)&&(i=h.payload.model),h.type==="event_msg"){let x=h.payload?.type;x==="task_started"?(l=!0,u=!1,f=h.payload?.turn_id||null):x==="task_complete"&&l&&(l=!1,!u&&h.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:vr,sourceId:h.payload?.turn_id||f||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i}));continue}if(h.type!=="response_item")continue;let w=h.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,Mu.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let y=f_(w.content),b=c.length?c.slice():null;c=[],!(!y&&!b)&&a.push({role:"assistant",text:y,tools:b||void 0,ts:h.timestamp?Date.parse(h.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}function p_(e){if(typeof e?.cmd=="string"&&e.cmd.trim())return e.cmd.trim();let t=e?.command;return typeof t=="string"&&t.trim()?t.trim():Array.isArray(t)&&t.length?t.length===3&&/^(ba)?sh$/.test(t[0])&&t[1]==="-lc"?String(t[2]).trim():t.map(String).join(" ").trim():null}function m_(e){let t=e[0];if(t!=='"'&&t!=="'"&&t!=="`")return null;let n="";for(let r=1;r<e.length;r++){let o=e[r];if(o==="\\"){let s=e[++r];if(s===void 0)return null;if(s==="n")n+=`
|
|
57
|
+
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(e[r+1]==="{"){let a=e.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(e.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(e.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===t)return n;if(t==="`"&&o==="$"&&e[r+1]==="{")return null;n+=o}}return null}function h_(e){let t=String(e||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(t);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=t.slice(s,a+1<n.length?n[a+1]:t.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=m_(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Rw(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await xr(e,t);if(!o)return[];let s;try{s=await et(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
58
|
+
`)){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"&&Hs.has(u.name)){let m;try{m=JSON.parse(u.arguments||"{}")}catch{continue}let h=p_(m);h&&(f=[h])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=h_(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((m,h)=>{a.push({id:d?h?`${d}:${h}`:d:null,tool:u.name,command:m,description:"",summary:Pn(m),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function $w(e,t){let n=await xr(e,t);if(!n)return null;try{return Math.round((await Cu.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var i_,d_,f_,Hs,Mu,vr,Sr=O(()=>{vt();yo();i_=process.env.BOSTRAT_CODEX_SESSIONS||Ou.join(process.env.CODEX_HOME||Ou.join(a_.homedir(),".codex"),"sessions");d_=new Set(["output_text","text"]),f_=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&d_.has(t.type)).map(t=>t.text).join(`
|
|
59
|
+
`).trim():"";Hs=new Set(["exec_command","shell","exec"]),Mu=new Set(["wait","write_stdin"]),vr="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 Ws({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Ow.has(r))return Ow.get(r);let o=Number(t)||0;if(o===401)return"auth";if(o===403)return"entitlement";if(o===429)return"rate_limit";if(o===529)return"overloaded";if(o>=500)return"server";let s=String(n||"").toLowerCase();return/overloaded/.test(s)?"overloaded":/rate limit|too many requests|quota|usage limit/.test(s)?"rate_limit":/billing|subscription|not allowed|disabled .* access|upgrade your plan|no credits/.test(s)?"entitlement":/log ?in|authenticat|expired|invalid.*key|unauthorized/.test(s)?"auth":/server|internal error|5\d\d/.test(s)?"server":"other"}function Cw(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Ws({error:t.code,status:t.status,text:e.text}),code:t.code||null,status:t.status||null,requestId:t.requestId||null}}if(e.failed){let t=String(e.text||"");return{cls:t===vr?"other":Ws({text:t}),code:null,status:null,requestId:null}}return null}function Mw(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Ws({text:n}),code:null,status:w_(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function w_(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var uU,Ow,Fi=O(()=>{Sr();uU=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Ow=new Map([["oauth_org_not_allowed","entitlement"],["permission_error","entitlement"],["billing_error","entitlement"],["authentication_error","auth"],["oauth_token_expired","auth"],["invalid_api_key","auth"],["rate_limit_error","rate_limit"],["overloaded_error","overloaded"],["server_error","server"],["api_error","server"],["internal_server_error","server"]])});function g_(e,t){return!e||!t?null:Zt(e,t)}function y_(e){return typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
|
|
60
|
+
`).trim():""}function b_(e){if(!Array.isArray(e))return null;let t=e.filter(n=>n&&n.type==="tool_use").map(n=>n.name).filter(Boolean);return t.length?t:null}function k_(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function Nu(e){let t=[],n=e?.message?.content;if(!Array.isArray(n))return t;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)t.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&&t.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return t}function Nw(e,t){if(!e.uuid)return null;let n=Nu(e).map((r,o)=>({uuid:e.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&t.get(r.toolUseId)||null}));return n.length?n:null}async function Pu(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await et(e,t)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
61
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.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 w of l.content){let y=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof y=="string"&&x_.test(y)&&s.set(w.id,y.split("/").pop())}if(k_(l.content)){let w=Nw(c,s);w&&o.push({role:"assistant",text:"",images:w,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=y_(l.content),d=u==="assistant"?b_(l.content):null,m=u==="user"?Nw(c,s):null;if(!f&&!d&&!m)continue;let h=u==="assistant"&&c.isApiErrorMessage?{code:typeof c.error=="string"?c.error:null,status:Number(c.apiErrorStatus)||null,requestId:c.requestId||null,cls:Ws({error:c.error,status:c.apiErrorStatus,text:f})}:null;o.push({role:u,text:f,tools:d||void 0,images:m||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null,apiError:h||void 0})}return o.slice(-n)}async function Pw(e,t,{turns:n=16}={}){let r=g_(e,t);if(!r)return"";let o=await Pu(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}:** ${v_(s.text||c,700)} ${i}`.trim()}).join(`
|
|
62
|
+
|
|
63
|
+
`):""}var x_,v_,Ui=O(()=>{Fi();vt();x_=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;v_=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import S_ from"node:fs/promises";import{execFile as T_}from"node:child_process";import{promisify as A_}from"node:util";function Lw(e){Du=typeof e=="function"?e:null}function __(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function Fw({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||Bw.has(r)||(Bw.add(r),Dw=Dw.then(()=>I_({sessionName:e,from:t,to:r})).catch(()=>{}))}async function I_({sessionName:e,from:t,to:n}){await z({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await Bu("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await S_.realpath(r),s=__(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Bu("npm",["install","-g","@openai/codex"],{env:a,timeout:E_});let i=(await Bu(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await z({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await jw({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await z({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await jw({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function jw(e){if(Du)try{await Du(e)}catch{}}var Bu,E_,Bw,Dw,Du,ju=O(()=>{Yt();Bu=A_(T_),E_=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Bw=new Set,Dw=Promise.resolve(),Du=null});function $_(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
64
|
+
`);if(!Uw.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(R_);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"&&Uw.test(a)?a:"The agent is asking for approval";return{provider:t,box:n,promptId:`${t}:${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 Tr(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
65
|
+
`);for(let r=n.length-1;r>=0;r--)if(qw.test(n[r]))return n.slice(r).join(`
|
|
66
|
+
`);return t}function O_(e){let t=String(e).trim();if(!t)return!1;let n=(t.match(/[A-Za-z]/g)||[]).length;return n>=3&&n/t.length>=.5}function C_(e){return/device code/i.test(e)?"works from the device you're reading this on":/sign in with chatgpt/i.test(e)?"opens a browser link on the node itself \u2014 can't finish from another device":""}function M_(e,t=""){let n=Tr(e);if(qs.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
67
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Hw);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let m=r[d].trim();if(m){if(Hw.test(r[d])||/^Press enter/.test(m))break;f=m;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:f})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!qw.test(r[l])&&O_(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:t,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=C_(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function B_(e,t=""){let n=Tr(e);if(qs.test(n)||!N_.test(n))return null;let r=n.trimEnd().split(`
|
|
68
|
+
`).pop()||"";return P_.test(r)?{provider:"codex",box:t,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 j_(e,t=""){let n=Tr(e);if(qs.test(n)||!Ww.test(n))return null;let r=n.split(`
|
|
69
|
+
`),o=r.findIndex(i=>Ww.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(D_);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:t,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 L_(e,t=""){let n=Tr(e);return qs.test(n)||!/open the following link to authenticate/.test(n)?null:{provider:"codex",box:t,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 F_(e,t=""){let n=Tr(e);return qs.test(n)||!/Paste or type your API key/.test(n)?null:{provider:"codex",box:t,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 U_(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Lu.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
70
|
+
`).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:t,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 W_(e,t=""){let n=String(e);if(!H_.test(n)||Lu.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
71
|
+
`),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:t,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 q_(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Lu.test(n)?null:{provider:"cursor",box:t,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 Kw(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=B_(t,n)||j_(t,n)||L_(t,n)||F_(t,n)||M_(t,n);if(r)return r}if(e==="cursor"){let r=W_(t,n)||q_(t,n)||U_(t,n);if(r)return r}return $_(t,{provider:e,box:n})}function Gw(e,t){let n=Array.isArray(t?.[0])?t[0]:[],o=(Number.isInteger(n[0])&&n[0]>=0?n[0]:0)+1;return String(o)}var R_,Uw,qs,qw,Hw,N_,P_,Ww,D_,Lu,H_,Hi=O(()=>{R_=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Uw=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;qs=/(^|\n)\s*›/,qw=/^\s*Welcome to Codex\b/;Hw=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;N_=/Successfully logged in\./,P_=/^\s*Press Enter to continue\.\s*$/;Ww=/Enter this one-time code/,D_=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Lu=/Run Everything|Add a follow-up|Plan, search, build anything/;H_=/click this link to log in|Signing in with the browser/});function J_(e){let t=String(e||"");if(!Le.codex.block.test(t))return null;let n=Tr(t);if(!Le.codex.block.test(n))return null;let r=n.match(z_);return{from:r?.[1]||null,to:r?.[2]||null}}function Bn(e){return new Promise(t=>setTimeout(t,e))}function Vw(e,t){let n=Date.now(),r=Y_();for(let[s,a]of Wi)n-a>=r&&Wi.delete(s);let o=`${e}::${t}`;return Wi.has(o)?!1:(Wi.set(o,n),!0)}function Q_(e){let t=0;for(let n of String(e).split(`
|
|
72
|
+
`))Z_.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Uu(e,t){let n=Le[e];if(!n?.busy)return{state:"idle",reason:null};if(Wu(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(t))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=Q_(t);return e==="claude"&&r>0&&r<X_?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Hu(e,t){return Uu(e,t).state}function Jw(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
73
|
+
`).slice(-tI).join(`
|
|
74
|
+
`);return eI.test(n)}function Gs(e,t){return Hu(e,t)==="busy"}function qi(e,t){return Hu(e,t)!=="idle"}function Ki(e,t){let n=Le[e];return n?.up?n.up.test(t):!0}function nI(e,t){let n=Le[e];return n?Wu(e,t)?!1:n.up.test(t)&&Hu(e,t)==="idle":!0}function Wu(e,t){let n=Le[e];return n?n.auth?.test(t)&&!n.up.test(t)?!0:n.block?.test(t)?e==="codex"?!0:!n.up.test(t):!1:!1}async function zw(e,t,n,{bailOnDialog:r=!1}={}){let o=Date.now()+n,s=0;for(await kr(e).catch(()=>{});;){let a=await Ae(e);if(nI(t,a)){await Bn(Ks);return}if(r&&t==="claude"&&V_.test(a)&&!Le.claude.up.test(a)){z({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="cursor"&&G_.test(a)&&!Le.cursor.up.test(a)&&s<3){if(!Vw(e,"cursor-trust")){await Bn(Fu);continue}s+=1,await z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:s}).catch(()=>{}),await je(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Bn(Ks);continue}if(t==="codex"&&s<3){let i=J_(a);if(i){if(!Vw(e,`codex-update:${i.from}->${i.to}`)){await Bn(Fu);continue}s+=1,await z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:s,update:`${i.from} -> ${i.to}`}).catch(()=>{}),await je(e,"3",{enter:!1,kind:"codex-update-skip"}),Fw({sessionName:e,from:i.from,to:i.to}),await Bn(Ks);continue}}if(Date.now()>o){if(Wu(t,a)){let i=`${t} is blocked on an interactive prompt`;throw t==="codex"?i=Le.codex.auth.test(a)?"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":t==="cursor"&&Le.cursor.auth.test(a)?i="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":t==="claude"&&Le.claude.auth.test(a)?i="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":t==="claude"&&Le.claude.block.test(a)&&(i='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'),z({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}if(await fo(e)){let i=`${t} 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 z({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),z({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,paneBlocked:!!Le[t]?.block?.test(a),paneUp:!!Le[t]?.up?.test(a),paneBusy:!!Le[t]?.busy?.test(a)}).catch(()=>{}),await Bn(Ks);return}await Bn(Fu)}}function Yw({agent:e,agentCmd:t}={}){let n=t?Cn(t):Cn(e||"");return{label:n,realClaude:t?br(t):n==="claude",realCodex:t?/codex/.test(t)&&!/fake-codex|fake-lead/.test(t):n==="codex",realCursor:t?/cursor/.test(t)&&!/fake-cursor|fake-box/.test(t):n==="cursor"}}function Vs({cwd:e,sessionName:t,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Yw({agent:n,agentCmd:r});return!t||!o&&!s?null:Dn({cwd:e,sessionName:t,agent:n,agentCmd:r}).catch(()=>{})}async function Dn({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=K_(),bailOnDialog:a=!1}={}){let{label:i,realClaude:c,realCodex:l,realCursor:u}=Yw({agent:r,agentCmd:o});if(c&&e&&await En(e),l&&e&&await to(e),u&&e&&await no(e),(c||l||u)&&n){await zw(n,i,s,{bailOnDialog:a});return}let f=Le[r]?r:Le[i]?i:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f){await zw(n,f,s);return}await Bn(Ks)}var K_,Fu,Ks,Le,G_,V_,z_,Y_,Wi,X_,Z_,eI,tI,Gi=O(()=>{ze();Es();wi();gi();ju();Hi();Yt();K_=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Fu=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Ks=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Le={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/}},G_=/Trust this workspace|\[a\]\s*Trust/,V_=/Resume from summary/i,z_=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;Y_=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Wi=new Map;X_=68,Z_=/^\s*[─━]{10,}\s*$/;eI=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,tI=15});function Mt(e){return e==="codex"?{enter:!0,submitDelayMs:rI}:e==="claude"?{enter:!0,submitDelayMs:oI}:{enter:!0}}var rI,oI,zs=O(()=>{rI=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),oI=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})()});function Ar(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of sI){let o=0;for(let s of aI[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Xw(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))iI.has(r)&&(t+=o||0),cI.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Js(e){return String(e||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var sI,aI,iI,cI,qu=O(()=>{sI=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],aI={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/]};iI=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),cI=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function tg(e){let t=Js(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||lI.has(r)||n.push(r);return n}function uI(e){let t=[];for(let n of e||[]){let r=n.payload||{},o=String(r.text||"").trim();if(n.type==="turn.user"){if(n.source==="hook"||!o)continue;t.push(eg(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(eg(n,"assistant",o))}}return t}function eg(e,t,n){let r=Ar(n);return{id:e.id,seq:e.seq,ts:e.payload?.ts??e.ts,role:t,boxId:e.boxId??null,provider:e.provider??null,text:n,tags:r?[r.category]:[]}}function Ys(e){let t=uI(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=tg(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=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function Vi(e,t,n={}){let{k:r=8,role:o=null,tag:s=null,boxId:a=null}=n;if(!e||!e.N)return[];let i=[...new Set(tg(t))];if(!i.length)return[];let c=new Map;for(let u of i){let f=e.postings.get(u);if(!f)continue;let d=f.size,m=Math.log(1+(e.N-d+.5)/(d+.5));for(let[h,w]of f){let y=e.lengths[h],b=w+Zw*(1-Qw+Qw*(y/(e.avgdl||1))),x=m*(w*(Zw+1)/b);c.set(h,(c.get(h)||0)+x)}}let l=[];for(let[u,f]of c){let d=e.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 Zw,Qw,lI,Ku=O(()=>{qu();Zw=1.5,Qw=.75,lI=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 zi from"node:fs/promises";import ng from"node:path";async function dI(e){let t=ng.join(e,".bostrat","events"),n;try{n=await zi.readdir(t)}catch{return Ys([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await zi.readFile(ng.join(t,o),"utf8");for(let a of s.split(`
|
|
75
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Ys(r)}function fI(e){let t=String(e||""),n=a=>{let i=t.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}
|
|
76
|
+
${o}`.trim()||t.slice(0,2e3)}function pI(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Vi(e,t,{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?["",`## ${rg}, 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(`
|
|
77
|
+
`):""}async function og(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await zi.readFile(t,"utf8");if(a.includes(rg))return{appended:!1,reason:"already-enriched"};let i=await dI(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=pI(i,fI(a),{k:r,maxChars:o,exclude:s});return c?(await zi.appendFile(t,`
|
|
78
78
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
79
|
-
- `).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
|
|
80
|
-
`),
|
|
81
|
-
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function
|
|
82
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
83
|
-
`).map(
|
|
79
|
+
- `).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 rg,sg=O(()=>{Ku();rg="Relevant prior discussion (verbatim, retrieved"});import Gu from"node:fs/promises";import bo from"node:path";import mI from"node:os";import{EventEmitter as hI}from"node:events";import{randomUUID as wI}from"node:crypto";function ko(e){return e?.cwd?bo.join(e.cwd,".bostrat","events"):bo.join(Vu,"events")}async function kI(e,t){if(!Ji.has(t)){let o=(await B(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Ji.set(t,o)}let n=Ji.get(t)+1;return Ji.set(t,n),n}function xI(e,t){let n=Array.isArray(e?.epochs)?e.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(t))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=t)return r}return 0}}async function I(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!gI.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!yI.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!bI.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?bo.join(Vu,"events"):ko(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let c=e.boxes.find(l=>l.name===t.boxId);o=xI(c,Number(t.payload?.ts??t.ts)||Date.now())}let s=await kI(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||wI(),trackId:n,boxId:t.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:t.ts??Date.now(),type:t.type,actor:t.actor??"system",provider:t.provider??null,source:t.source??"orchestrator",payload:t.payload??{},...t.causationId?{causationId:t.causationId}:{},...t.correlationId?{correlationId:t.correlationId}:{}};return await Gu.mkdir(r,{recursive:!0}),await Gu.appendFile(ag(r,n),JSON.stringify(i)+`
|
|
80
|
+
`),tt.emit("event",i),tt.emit(`track:${n}`,i),i}async function B(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?bo.join(Vu,"events"):ko(e));let o;try{o=await Gu.readFile(ag(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
81
|
+
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function ig(e,t){let n=`track:${e}`;return tt.on(n,t),()=>tt.off(n,t)}var gI,yI,bI,tt,Vu,ag,Ji,le=O(()=>{gI=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","provider.error","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","loop.create.offered","loop.create.answered","loop.created","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),yI=new Set(["user","agent","orchestrator","system","peer"]),bI=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),tt=new hI;tt.setMaxListeners(0);Vu=process.env.BOSTRAT_STATE_DIR||bo.join(process.env.HOME||mI.tmpdir(),".bostrat");ag=(e,t)=>bo.join(e,`${t}.ndjson`),Ji=new Map});function Xs(e,t){if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
82
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(cg.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(cg.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(vI);if(!a)return null;let i=[a[1].trim(),...s.slice(1).map(c=>c.trim())].filter(Boolean);return i.length?i.join(" "):null}function Ne(e){return String(e||"").replace(/\s+/g," ").trim()}function lg(e,t,{slackMs:n=2e4}={}){let r=Ne(t?.text);return!r||typeof t?.at!="number"?!1:(Array.isArray(e)?e:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=t.at-n&&Ne(o.text).includes(r))}function ug(e,t,n,{busy:r=!1}={}){let o=Xs(e,n);return o&&zu(t,o)?"stuck":r||o?"submitted":"unknown"}function zu(e,t){let n=Ne(e),r=Ne(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var cg,vI,Ju=O(()=>{cg=/^\s*─{8,}\s*$/,vI=/^❯[ ]?(.*)$/});function II(e){return SI.test(String(e||"").trimStart())}function pg(e){let t=String(e||"");if(!II(t.trimStart()))return null;let n=t.replace(TI,"").trim(),r=RI(n),o=[];for(let u of EI){let f=u.from.map(d=>xo(r.get(d))).find(d=>d&&!dg(d));f&&o.push({h:u.h,text:Zs(f,AI)})}let s=xo(r.get("Current Work"))||xo(r.get("Primary Request and Intent"))||OI(n),a=Xi(s),i=Zs(Yi(Zu(a[0]||"")),_I)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Zs(Yi(Zu(l)),Xu));for(let u of["Pending Tasks","Optional Next Step"]){let f=Xi(xo(r.get(u)))[0]||"";if(!(!f||dg(f))){c.push(Zs(`Left to do: ${Yi(Zu(f))}`,Xu));break}}if(!c.length){let u=Xi(xo(r.get("Problem Solving"))||"")[0];u&&c.push(Zs(Yi(u),Xu))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function RI(e){let t=new Map,n=[];Yu.lastIndex=0;for(let r=Yu.exec(e);r;r=Yu.exec(e))n.push({name:$I(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??e.length;t.has(n[r].name)||t.set(n[r].name,e.slice(n[r].end,o).trim())}return t}function $I(e){return fg.find(n=>n.toLowerCase()===e.toLowerCase())||e}function xo(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
83
|
+
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function OI(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return xo(t.split(/\n{2,}/).slice(0,2).join(" "))}function Xi(e){let t=String(e||"").trim();if(!t)return[];let r=t.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 Zu(e){return String(e||"").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)/,t=>t.toUpperCase()).trim()}function Yi(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function dg(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function Zs(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Xi(n),o="";for(let i of r){let c=o?`${o} ${i}`:i;if(c.length>t)break;o=c}if(o.length>=Math.min(60,t*.4))return o;let s=n.slice(0,t),a=s.lastIndexOf(" ");return`${(a>t*.6?s.slice(0,a):s).replace(/[,;:.\s]+$/,"")}\u2026`}var SI,fg,Yu,TI,AI,EI,_I,Xu,mg=O(()=>{SI=/^This session is being continued from a previous conversation that ran out of context\./,fg=["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"],Yu=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${fg.join("|")})[:*\t ]*$`,"gim"),TI=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,AI=420,EI=[{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"]}],_I=116,Xu=168});function wg(e){return Qu.has(String(e||"").replace(/^\//,"").toLowerCase())}function Zi(e){let t=String(e||"").trim();if(!t)return null;let n=pg(t);if(n)return n;let r=t.match(CI);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(MI);if(o)return{kind:"skill",name:o[1]};let s=t.match(NI);if(s){let i=s[1].toLowerCase();return Qu.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(qI);if(a&&Qu.has(a[1].toLowerCase()))return{kind:"suppress"};if(PI.test(t))return{kind:"suppress"};if(DI.test(t))return{kind:"suppress"};if(BI.test(t))return{kind:"suppress"};if(jI.test(t)){let i=l=>KI(t.match(l)?.[1].trim()||"")||null,c=i(WI);return{kind:"task",summary:i(LI)||"",taskId:i(FI),toolUseId:i(UI),status:i(HI)?.toLowerCase()||null,resultHead:c?c.length>hg?`${c.slice(0,hg).trimEnd()}\u2026`:c:null}}return null}function KI(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function gg(e){return e?.kind==="skill"?`Ran the ${e.name} skill`:e?.kind==="command"?`Ran ${e.command}`:e?.kind==="task"?e.summary||"A background task finished":e?.kind==="resume-summary"?e.title?`Context resumed from summary: ${e.title}`:"Context resumed from summary":""}function yg(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var CI,MI,NI,PI,BI,DI,jI,LI,FI,UI,HI,WI,hg,Qu,qI,ed=O(()=>{mg();CI=/^Base directory for this skill:\s*(\S+)/,MI=/^Run the "([^"]+)" workflow\./,NI=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,PI=/^<local-command-(?:stdout|caveat)>/i,BI=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,DI=/^Continue from where you left off\.$/,jI=/^<task-notification>/i,LI=/<summary>([\s\S]*?)<\/summary>/i,FI=/<task-id>([\s\S]*?)<\/task-id>/i,UI=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,HI=/<status>([\s\S]*?)<\/status>/i,WI=/<result>([\s\S]*?)(?:<\/result>|$)/i,hg=600,Qu=new Set(["model","clear","compact","login","exit","quit"]);qI=/^\/([a-z0-9:_-]+)$/i});import GI from"node:crypto";function YI(e){let t=Array.isArray(e)?e:Array.isArray(e?.questions)?e.questions:null;if(!t||!t.length)return null;let n=[];for(let r of t.slice(0,kg)){let o=Qs(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,VI)){let c=Qs(i?.label);if(!c)return null;let l=Qs(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<zI)return null;n.push({question:o,header:Qs(r?.header).slice(0,JI),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function nd(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;bg.lastIndex=0;let t=null;for(let o;o=bg.exec(e);)t=o;if(!t||t.index+t[0].length<e.trimEnd().length)return null;let n;try{n=JSON.parse(t[2])}catch{return null}let r=YI(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function rd(e,t,n){let r=GI.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function od(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=kg}={}){if(!Array.isArray(e)||!t||!td.has(n))return null;let s=null;for(let l=e.length-1;l>=0;l--){let u=e[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==t||s.agent!==n)return null;let a=s.ask?.questions||nd(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=rd(n,s.sourceId||s.id,a);return typeof r=="function"&&r(c)?null:{provider:n,box:t,promptId:c,groupId:c,answerMode:"say",source:"transcript",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function xg(e,t,n){let r=[];(e?.questions||[]).forEach((a,i)=>{let c=(t?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=Qs(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 td,kg,VI,zI,JI,bg,Qs,vg,sd=O(()=>{td=new Set(["cursor","codex"]),kg=4,VI=4,zI=2,JI=24,bg=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Qs=e=>typeof e=="string"?e.trim():"";vg=`
|
|
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,70 +103,70 @@ 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 DI(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 LI(t,e,n){let r=hg(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 br(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 m=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let S of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let $=S[0].split("/").pop().toLowerCase();d.has($)||d.set($,[]),d.get($).push(g.payload?.ts??g.ts)}}let f=3*6e4,p=20*6e4,h=(g,m)=>!!(g?.name&&d.get(String(g.name).toLowerCase()))?.some($=>$>=m-f&&$<=m+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let m=$t(g.payload?.text);m&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(m))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let m=$t(g.payload?.text);m&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(m))}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 m=g.payload.note,S={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:rt(g)};m.toolUseId&&i.set(m.toolUseId,S);let $=String(m.summary||"").match(u);$&&l.set($[1],S)}let w=new Map,y=new Map,k=null,x=new Map,A=new Map,R=new Map,V=new Map,M=new Map,N=new Map,T=new Map,D=new Map,F=new Map,ct=new Map,Q=new Map,_=new Map,U=new Map,j=()=>{for(let g of U.values())g.questions.length&&e.push(g);U.clear()},at=null,Et=6e4,Lt=new Map,it=(g,m)=>{let S=o.get(g);if(!S?.length)return!1;let $=$t(m),lt=s.get(g);return S.some(ht=>{if(!$.startsWith(ht))return!1;let vt=$t($.slice(ht.length));return!!vt&&!!lt?.has(vt)})};for(let g of t||[]){let m=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length)break;{let S=m.note||qi(m.text);if(S){if(S.kind==="task"){if(S.toolUseId&&a.has(S.toolUseId))break;let $=String(S.summary||"").match(u);if(!S.toolUseId&&$&&c.has($[1]))break}if(S.kind==="command"&&cg(S.command))break;if(S.kind==="resume-summary"){e.push({id:g.id,kind:"resume-summary",title:S.title||"Context resumed from summary",bullets:S.bullets||[],sections:S.sections||[],rawChars:S.rawChars??null,ts:rt(g),box:g.boxId,agent:g.provider});break}S.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:ug(S),name:S.name??null,command:S.command??null,summary:S.summary??null,status:S.status??null,ts:rt(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${rt(g)}`)||g.source==="transcript"&&it(g.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:rt(g),box:g.boxId,agent:g.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"){m.kind==="prompt-answer"&&(at=null);break}for(let S of x.values())S.status="stale";x.clear();for(let S of R.values())S.status="stale";R.clear(),at&&at.box===g.boxId&&(e.push(DI(at)),at=null),e.push({id:g.id,kind:"message",role:"user",text:m.text,ts:rt(g),box:g.boxId,agent:g.provider,boxRole:m.boxRole,...g.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let S=m.from||{};e.push({id:g.id,kind:"event",type:"peer-message",text:m.text,from:S.trackName||S.boxId||S.name||null,fromTrackId:S.trackId||null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let S=Aw(m,{codexFailedText:hr});if(S){e.push({id:g.id,kind:"event",type:"provider-error",cls:S.cls,status:S.status??void 0,text:S.text,provider:g.provider,legacy:!0,at:rt(g),ts:rt(g),box:g.boxId,agent:g.provider});break}j();let $=(m.images||[]).filter(Ct=>!h(Ct,rt(g)));if(!String(m.text||"").trim()&&!$.length)break;if(g.provider==="cursor"){let Ct=`${g.boxId}|${m.text}`;if(n.has(Ct))break;n.add(Ct)}(!at||at.box===g.boxId)&&(at=null);let lt=g.provider==="codex"?(m.tools||[]).filter(Ct=>!Eu.has(Ct)&&!Ps.has(Ct)):m.tools||[],ht=m.text,vt=null;if(zu.has(g.provider)){let Ct=Ju(String(m.text||""));Ct&&(ht=Ct.text,vt={questions:Ct.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:ht,ts:rt(g),box:g.boxId,agent:g.provider,boxRole:m.boxRole,...vt?{ask:vt}:{},...lt.length?{tools:lt}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...$.length?{images:$}:{}}),vt&&(at={id:g.id,groupId:Yu(g.provider,m.sourceId||g.id,vt.questions),provider:g.provider,box:g.boxId,questions:vt.questions,ts:rt(g)});break}case"job.started":{if(!m.id)break;let S=rt(g),$=i.get(m.id);if(!$&&(m.kind||"agent")==="agent"&&m.name){let b=l.get(m.name);b&&b.ts>=S&&($=b)}let lt={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:$?$.status==="killed"?"stopped":$.status&&$.status!=="completed"?"failed":"done":"running",startedTs:S,finishedTs:$?.ts??null,summary:$?.summary??null,resultHead:$?.resultHead??null},ht=Lt.get(g.boxId),vt=ht?.jobs[ht.jobs.length-1]?.startedTs;if(ht&&vt!=null&&S-vt<=Et)ht.jobs.push(lt);else{let b={id:g.id,kind:"job-dock",dockId:m.id,ts:S,box:g.boxId,agent:g.provider,jobs:[lt]};Lt.set(g.boxId,b),e.push(b)}break}case"handoff.done":e.push(de(g,"lead-handoff"));break;case"subtask.delegated":e.push({...de(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(de(g,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let $ of x.values())$.status="stale";x.clear();for(let $ of R.values())$.status="stale";R.clear()}let S=de(g,`pr-${m.change}`);if(S.box=g.boxId??null,jI.has(m.change)&&m.number!=null){let $=LI(w,y,m);if(w.size>=2){for(let ht of y.values())for(let vt of ht)vt.grouped=!0;S.groupPrs=[...w.values()];let lt=[S];for(let ht of w.keys())y.set(ht,lt)}else{let lt=y.get($)??[];lt.push(S),y.set($,lt)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let $=hg(m),lt=k&&!k.complete||w.size>=2;if(w.has($))w.delete($),y.delete($);else if(m.number!=null)for(let ht of[...w.keys()])ht.endsWith(`#${m.number}`)&&(w.delete(ht),y.delete(ht));if(lt&&m.number!=null){(!k||k.complete)&&(k={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:rt(g),ts:rt(g),box:g.boxId,agent:g.provider},e.push(k)),k.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:rt(g)}),k.remaining=[...w.values()],k.complete=w.size===0;break}}e.push(S);break}case"pr.offered":{if(!m.offerId)break;for(let $ of x.values())$.status="stale";x.clear();for(let $ of R.values())$.status="stale";R.clear();let S={id:g.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};x.set(m.offerId,S),A.set(m.offerId,S),e.push(S);break}case"pr.offer.answered":{let S=A.get(m.offerId);S&&(S.status=m.action||"dismiss",S.actions=Array.isArray(m.actions)?m.actions:null,S.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(S.existingPr={number:m.existingPr.number??null}),x.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let $ of R.values())$.status="stale";R.clear();let S={id:g.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};R.set(m.offerId,S),V.set(m.offerId,S),e.push(S);break}case"pr.exists.answered":{let S=V.get(m.offerId);S&&(S.status=m.choice||"dismiss",R.delete(m.offerId));break}case"box.state.changed":{let S=m.change==="model"?"model-change":m.change;if(!S)break;let $=de(g,S);S==="model-change"&&($.to=m.model),e.push($);break}case"track.transferred":case"track.recovered":e.push(de(g,"track-moved"));break;case"view.state.changed":{if(m.change!=="created"||m.restarted)break;e.push({id:g.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:rt(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:rt(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(m.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:rt(g),box:g.boxId,agent:g.provider});break}if(m.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(m.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:rt(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(m.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:rt(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let $ of T.values())$.box===(m.box||g.boxId)&&($.status="stale",T.delete($.offerId));let S={id:g.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:rt(g),box:m.box||g.boxId,agent:m.agent||g.provider};T.set(m.offerId,S),D.set(m.offerId,S),e.push(S);break}case"box.fullscreen.answered":{let S=D.get(m.offerId);S&&(S.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",T.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let $ of M.values())$.key===m.key&&($.status="stale",M.delete($.offerId));let S={id:g.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};M.set(m.offerId,S),N.set(m.offerId,S),e.push(S);break}case"ticket.move.answered":{let S=N.get(m.offerId);S&&(S.status=m.action==="moved"?"moved":"dismiss",M.delete(m.offerId));break}case"ticket.state.changed":{for(let S of M.values())S.key===m.key&&S.to===m.to&&(S.status="stale",M.delete(S.offerId));if(m.mode==="offer")break;e.push(de(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:rt(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(de(g,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let $ of F.values())$.status="stale";F.clear();let S={id:g.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};F.set(m.proposalId,S),ct.set(m.proposalId,S),e.push(S);break}case"ticket.create.answered":{let S=ct.get(m.proposalId);S&&(S.status=m.action==="created"?"created":"dismiss",S.createdCount=m.createdCount??null,S.failedCount=m.failedCount??null,F.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let $ of Q.values())$.status="stale";Q.clear();let S={id:g.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,status:"open",ts:rt(g),box:g.boxId,agent:g.provider};Q.set(m.proposalId,S),_.set(m.proposalId,S),e.push(S);break}case"loop.create.answered":{let S=_.get(m.proposalId);S&&(S.status=m.action==="created"?"created":"dismiss",Q.delete(m.proposalId));break}case"loop.created":{let S=m.proposalId?_.get(m.proposalId):null;S&&(S.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null});break}case"ticket.created":{let S={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},$=m.proposalId?ct.get(m.proposalId):null;if($){$.created=S;break}e.push({id:g.id,kind:"ticket-created",...S,ts:rt(g),box:g.boxId,agent:g.provider});break}case"error":{if(!m.kind)break;e.push(de(g,m.kind));break}case"provider.error":e.push({...de(g,"provider-error"),provider:g.provider,box:g.boxId,agent:g.provider});break;case"usage.cap.hit":e.push(de(g,"usage-cap-hit"));break;case"usage.cap.warning":e.push(de(g,"usage-cap-warning"));break;case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:m.provider||g.provider,ts:rt(g),at:rt(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(at=null,m.control||!m.question)break;let S=m.groupId||m.promptId;if(!S)break;let $=U.get(S);$||($={id:g.id,kind:"prompt",groupId:S,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:m.questionTotal??1,questions:[],ts:rt(g)},U.set(S,$));let lt=m.questionIndex??0,ht={index:lt,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},vt=$.questions.findIndex(b=>b.index===lt&&$t(b.question)===$t(ht.question));vt>=0?$.questions[vt]=ht:$.questions.push(ht),$.questions.sort((b,E)=>b.index-E.index),$.questionTotal=Math.max($.questionTotal,m.questionTotal??1),$.questions.length>=$.questionTotal&&(e.push($),U.delete(S));break}default:break}}return UI(e.sort((g,m)=>(g.ts??0)-(m.ts??0)))}function UI(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<=FI){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 Qu(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:HI(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??br(t)).filter(o=>o.kind==="message");return gw(n,r)}function HI(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==fo(t.command)?e:Rn(t.command,t.description||"")}function de(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:rt(t),ts:rt(t)}}function wg(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)+WI,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&qI.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}function gg(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||!GI.has(r.type)||!r.box)continue;let o=(r.ts??0)+KI,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 rt,jI,hg,FI,WI,qI,KI,GI,td=C(()=>{po();wr();Ci();Hu();Vu();Zu();rt=t=>t.payload?.ts??t.ts;jI=new Set(["opened","ci-fixed","reconciled","pushed"]),hg=t=>`${t.repo??""}#${t.number}`;FI=2*6e4;WI=12e4,qI=new Set(["archived","unarchived","opened"]);KI=2500,GI=new Set(["pr-opened","pr-merged","pr-auto-armed"])});function XI(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 QI(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 tR(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<JI&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<YI&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function xg(t,{trackName:e="this Track",now:n=null,mode:r="failover"}={}){let o=XI(t),s=QI(t),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=br(t??[]).filter(f=>f?.kind==="message"&&typeof f.text=="string"&&f.text.trim()).slice(-VI);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${e}`),l.push(""),l.push(kg[r]??kg.failover),l.push(""),o.lastPush?.sha){let f=bg(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=bg(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=tR(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"}:** ${ZI(f.text,zI)}`),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 uR(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Ag.test(s)){if(r.length>=Pg)continue;let a=lR(s.replace(Ag,"").replace(/[.;,:]+$/,""),Bg,iR);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>Tg&&(o=`${o.slice(0,Tg-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Dg(t,e,{timeoutMs:n=9e4}={}){let r=Mn.join(e,sR),o=Mn.join(r,aR);await Cn.mkdir(r,{recursive:!0});let s=await Ki(o),a=await nt(t);if(!a)throw new Error(`session "${t}" not found`);return await Gi(t,a,cR(o)),await qg(o,s,n),uR(await Cn.readFile(o,"utf8"))}function jg(t,e){let n=(e||"").trim();if(!n)return!1;if(t==="assistant")return n===Og||n===Ng;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith($g)||n===nR||r.startsWith(Cg)||r.startsWith(Mg)}function Lg(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-dR;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 Fg(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 Ug(t,{windows:e=[]}={}){if(jg(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 Vi(t){return ed.get(t)||null}async function Hg(t){let e=ed.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await nt(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:Mn.join(n.cwd,_g,Ig)};return ed.set(t,r),mR(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function mR(t,e){t.step="summarize",await Wg(t.name,e),t.step="clear";let n=await nt(t.name);await Gi(t.name,n,"/clear"),await od(fR),t.step="resume",await Gi(t.name,n,oR),t.step="done",t.finishedAt=Date.now()}async function Wg(t,e,{timeoutMs:n=pR}={}){let r=Mn.join(e,_g),o=Mn.join(r,Ig);await Cn.mkdir(r,{recursive:!0});let s=await Ki(o),a=await nt(t);await z({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:o,beforeMtime:s}),await Gi(t,a,rR(o)),await qg(o,s,n),await z({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:o,afterMtime:await Ki(o)});let i=await Yw(e,o,{exclude:u=>jg(u.role,u.text)});await z({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 Cn.readFile(o,"utf8");await I({id:c,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Rg(u),ts:Date.now()}})}catch{}}await Nh(e);let l=await hR(r,o);return c&&await Kh(c,l||o,{box:t,agent:a?.agent??null}),o}async function nd(t,{timeoutMs:e}={}){let n=await nt(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);return Wg(t,n.cwd,e?{timeoutMs:e}:void 0)}async function rd(t,e,{trackName:n="this Track"}={}){let r=await B({id:t,cwd:e}),o=xg(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Mn.join(e,Eg);return await Cn.mkdir(Mn.dirname(s),{recursive:!0}),await Cn.writeFile(s,o,"utf8"),await I({id:t,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Rg(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Eg}async function Ki(t){try{return(await Cn.stat(t)).mtimeMs}catch{return 0}}async function qg(t,e,n){let r=Date.now()+n;for(;;){if(await Ki(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await od(2e3)}}async function hR(t,e){try{if(!eR.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Mn.join(t,`${n}.md`);return await Cn.copyFile(e,r),r}catch{return null}}function od(t){return new Promise(e=>setTimeout(e,t))}async function Gi(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 On({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await nt(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await _t(t,["Escape"],{kind:"drive-wipe"}),await od(150),await io(t,Ri,{kind:"drive-wipe"}),await Pt(t,n,{kind:"orchestration-drive",...Oe(r.agent)})}var _g,Ig,Sg,$g,Og,Cg,nR,rR,oR,Mg,Ng,sR,aR,Tg,Pg,Bg,iR,cR,Ag,lR,dR,fR,pR,ed,Eg,sd=C(()=>{Gt();ji();Ge();Us();Xw();wi();st();xi();vg();_g=".bostrat/handoff",Ig="latest.md",Sg=32*1024;$g="Checkpoint: write a work-in-progress handoff",Og="HANDOFF WRITTEN",Cg="Resume from .bostrat/handoff/latest.md",nR="/clear",rR=t=>`${$g} 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 ${Og} and take no further action.`,oR=`${Cg}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Mg="Beat: write a",Ng="BEAT WRITTEN",sR=".bostrat/arc",aR="beat.txt",Tg=200,Pg=4,Bg=5,iR=36,cR=t=>`${Mg} 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 ${Pg} 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 ${Ng} and take no further action.`,Ag=/^(?:[-–—*•·]|\d+[.)])\s+/,lR=(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};dR=6e5;fR=3500,pR=3e5,ed=new Map;Eg=".bostrat/handoff/status-latest.md"});function Gg(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!wR.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 gR(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:Gg(o.from,"from",n),toMs:Gg(o.to,"to",n)}))}return e}function bR(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 kR(t){if(!t)return null;let e=String(t).trim();return ad[e]?e:Kg[e]?Kg[e]:Object.keys(ad).find(r=>e.startsWith(r))||null}function Vg(t,e){let n=kR(t);if(!n)return null;let r=bR(e);return r===null?null:yR[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function Nn(t,e={},n){let r=Vg(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 ad,Kg,wR,yR,zg,Vs=C(()=>{ad={"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)."}]},Kg={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"},wR=/^\d{4}-\d{2}-\d{2}$/;yR=gR(ad);zg=(t,e)=>Vg(t,e)!==null});function xR(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function Yg(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function Je(t){let e=t.payload?.model||"unknown",n=xR(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=t.source==="vendor",s=r,a=!1;if(n&&!o){let l=Nn(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&&!zg(e,t.ts);return{usd:s,model:e,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function Pn(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=Jg();for(let f of n){let{usd:p,model:h,tokens:w,repriced:y,unpriced:k,asRecorded:x}=Je(f);i+=x;let A=f.provider||"unknown",R=f.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(h)),a+=p,Yg(d,w),r[h]||={usd:0,tokens:Jg(),events:0},r[h].usd+=p,r[h].events+=1,Yg(r[h].tokens,w),o[A]||={usd:0,events:0},o[A].usd+=p,o[A].events+=1,s[R]||={usd:0,events:0,provider:A,models:new Set,label:e[R]||R},s[R].usd+=p,s[R].events+=1,h!=="unknown"&&s[R].models.add(h),f.epoch!=null){let V=(s[R].epochs||={})[f.epoch]||={usd:0,events:0,provider:A,models:new Set};V.usd+=p,V.events+=1,V.provider=A,h!=="unknown"&&V.models.add(h)}}return{totalUsd:go(a),asRecordedUsd:go(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:zi(r,f=>({...f,usd:go(f.usd)})),byProvider:zi(o,f=>({...f,usd:go(f.usd)})),byBox:zi(s,f=>({usd:go(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:zi(f.epochs,p=>({usd:go(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Jg,go,zi,kr=C(()=>{Vs();Jg=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});go=t=>Math.round(t*1e4)/1e4,zi=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]))});function vR(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 Xg(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(id(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(id(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(`${id(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Pn(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?vR(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var id,Zg=C(()=>{kr();id=(t,e)=>`${t} ${e}${t===1?"":"s"}`});import{execFile as SR}from"node:child_process";import{promisify as TR}from"node:util";import{randomUUID as AR}from"node:crypto";async function cd(t,e){let n=Date.now()+e;for(;;){if(await ao(t))return!0;if(Date.now()>n)return!1;await Ji(_R)}}async function RR(t,e){let n=ty[e]||ty.default;n.escape&&(await _t(t,["Escape"],{kind:"swap-exit"}),await Ji(150)),n.wipe&&await io(t,Ri,{kind:"swap-exit"}),await $i(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Oe(e).submitDelayMs||0;await Ji(Math.max(r,250)),await _t(t,["Enter"],{kind:"swap-exit"})}async function $R(t,e){try{let{stdout:n}=await yo(L,H("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ey(t){await yo(L,H("respawn-pane","-k","-t",t))}async function ld(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 nt(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||En(n),a=Number(e.exitTimeoutMs)||ER;await z({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await ao(t)&&(await RR(t,o),!await cd(t,a)&&(i=!0,await z({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await ey(t),!await cd(t,Qg))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&fr(n),l=c?ai():null,u=await $R(t,"ANTHROPIC_API_KEY"),d=!1;try{l?(await yo(L,H("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await yo(L,H("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),d=!0)}catch(y){throw l?Si(y,l):y}if(d&&!i&&(await ey(t),!await cd(t,Qg)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,p=e.resumeSessionId||AR(),h=f?n:fu(n,p,t),w=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await kn(r.cwd),s==="codex"&&await Yr(r.cwd),s==="cursor"&&await Xr(r.cwd),await pr(t),await yo(L,H("set-option","-t",t,"@bostrat-sid",p)),await yo(L,H("set-option","-t",t,"@bostrat-agent",s)),await Ji(IR),await Pt(t,h,{kind:"swap-launch"}),await z({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 yo,Ji,ER,Qg,_R,IR,ty,ny=C(()=>{Gt();bs();ci();li();Us();Ge();yo=TR(SR),Ji=t=>new Promise(e=>setTimeout(e,t)),ER=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Qg=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,_R=250,IR=400,ty={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 ry(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Zt(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()||""):
|
|
127
|
-
${String(
|
|
128
|
-
`).filter(Boolean);return
|
|
129
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
130
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
131
|
-
`),n=
|
|
132
|
-
`),n=
|
|
133
|
-
`);for(let n=
|
|
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 ib(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&&!ob.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=sb.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!R$.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Xs(t,e,{timeoutMs:n=_$}={}){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 sc(I$)}}async function C$(t,e){return await Pt(t,`/model ${e.model}`,Oe("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function M$(t,e){await Ii(t),await Pt(t,"/model",{enter:!1}),await sc(250),await _t(t,["Enter"],{kind:"model-switch"});let n=await Xs(t,tb);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await _t(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=O$(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await Pt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await Xs(t,i=>eb(i)||!tb(i)&&o(i)&&{done:o(i)});if(!s)throw await _t(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?ob.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 _t(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await Pt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Xs(t,i=>!eb(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 N$(){let t=Qy.join(process.env.CURSOR_CONFIG_DIR||Qy.join(E$.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await A$.readFile(t,"utf8"))?.model||null}catch{return null}}async function P$(t,e){await Ii(t),await $i(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await Xs(t,nb),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 Xs(t,nb,{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 _t(t,["Down"],{kind:"model-switch"}),await sc(250)}if(!n||!s||!o(s)){await _t(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await _t(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 _t(t,["Enter"],{kind:"model-switch"}),await sc(800);let a=await N$();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 cb(t,e,n){return t==="codex"?M$(e,n):t==="cursor"?P$(e,n):C$(e,n)}var sc,_$,I$,rb,R$,$$,ob,sb,lb=C(()=>{Gt();Us();sc=t=>new Promise(e=>setTimeout(e,t)),_$=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,I$=300,rb=[{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"}],R$=new Set([...rb.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"]),$$=[{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"}],ob=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),sb=[{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 ic(t){let e=t||{},n=B$(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function B$(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:te(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:te(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:te(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:te(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:te(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:te(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:te(t.agent),payload:{...Rr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:te(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Rr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:te(t.agent),payload:{change:"subtask-resumed",...Rr(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",...Rr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Rr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:te(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:te(t.agent),payload:{...Rr(t)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Rr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:te(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}function fb(t,e){let n=ic(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;qd(()=>I(r,n))}async function pb(t){if(!(!t?.id||ub.has(t.id))){ub.add(t.id);try{let e=await B(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=ic(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(t,o)}}catch{}}}function Rr(t){let{type:e,at:n,...r}=t;return r}function mb(t,e,n){!n?.promptId||Wd.get(t.id)===n.promptId||(Wd.set(t.id,n.promptId),qd(()=>I(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:te(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 Kd(t,e,n,r={}){Wd.delete(t.id),qd(()=>I(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:te(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}var te,ac,qd,db,ub,Wd,Gd=C(()=>{st();te=t=>t||null;ac=Promise.resolve(),qd=t=>(ac=ac.then(t).catch(()=>{}),ac),db=()=>ac;ub=new Set;Wd=new Map});function U$(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function kb(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of U$(n).reverse()){if(!D$.test(r))continue;let o=r.endsWith("?")&&r.length<=F$&&L$.test(r);if(j$.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function xb(t){let e=String(t||""),n=[];H$.test(e)&&n.push("commit"),W$.test(e)&&n.push("rebase"),q$.test(e)&&n.push("push");let r=G$.test(e);!r&&K$.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 Io(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)`:"",h=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${h}${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${h}${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 vb(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 Sb(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<=V$&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Tb(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+z$||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Ab(t){let e=(Array.isArray(t)?t:[]).filter(r=>hb[r]).map(r=>hb[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 wb,gb,yb,bb,D$,j$,L$,F$,cc,H$,W$,q$,K$,G$,V$,z$,hb,Ro=C(()=>{wb=/(?<!\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,gb=/(?<!\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,yb=/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,bb=/(?<!\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,D$=new RegExp(`\\b(?:${wb}|${gb}|${yb}|${bb})\\b`,"i"),j$=/\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,L$=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,F$=90;cc=["commit","rebase","push","pr"],H$=new RegExp(`\\b(?:${wb})\\b`,"i"),W$=new RegExp(`\\b(?:${bb})\\b`,"i"),q$=new RegExp(`\\b(?:${gb})\\b`,"i"),K$=new RegExp(`\\b(?:${yb})\\b`,"i"),G$=/\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;V$=3e4;z$=2500;hb={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import J$ from"node:fs";import Vd from"node:fs/promises";import Rb from"node:path";import Y$ from"node:os";function Cb(t,e=!1){if(!e&&t-_b<X$)return;_b=t;let n=JSON.stringify({windowStart:$o,byCaller:Object.fromEntries(Xe),lastHour:jn});Ib=Ib.then(()=>Vd.mkdir($b,{recursive:!0})).then(()=>Vd.writeFile(`${lc}.tmp`,n)).then(()=>Vd.rename(`${lc}.tmp`,lc)).catch(()=>{})}function Mb(t){t-$o<Ob||(jn={start:$o,total:uc(Xe),byCaller:Object.fromEntries(Xe)},jn.total&&console.log(`[github] ${jn.total} calls last hour`,jn.byCaller),$o=t,Xe=new Map,Cb(t,!0))}function Ze(t="other",e=Date.now()){Mb(e),Xe.set(t,(Xe.get(t)||0)+1),Cb(e)}function Nb(t=Date.now()){return Mb(t),{windowStart:$o,total:uc(Xe),byCaller:Object.fromEntries(Xe),lastHour:jn,backoffUntil:$r>t?$r:null}}function Ln(t){return Z$.test(String(t?.message||t||""))}function Fn(t,e=Date.now()){return $r<=e&&console.warn(`[github] rate limited \u2014 backing off ${Eb/6e4} min:`,String(t?.message||t||"").slice(0,200)),$r=Math.max($r,e+Eb),$r}function dc(t=Date.now()){return Math.max(0,$r-t)}var $b,lc,Ob,Eb,X$,$o,Xe,jn,$r,_b,uc,Ib,Z$,Zs=C(()=>{$b=process.env.BOSTRAT_STATE_DIR||Rb.join(process.env.HOME||Y$.tmpdir(),".bostrat"),lc=Rb.join($b,"github-metrics.json"),Ob=60*6e4,Eb=5*6e4,X$=6e4,$o=Date.now(),Xe=new Map,jn=null,$r=0,_b=0,uc=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(J$.readFileSync(lc,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Ob?($o=t.windowStart,Xe=new Map(Object.entries(t.byCaller||{})),jn=t.lastHour||null):uc(new Map(Object.entries(t.byCaller||{})))&&(jn={start:t.windowStart,total:uc(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}Ib=Promise.resolve();Z$=/rate limit|too quickly|abuse detection|HTTP 429/i});import Q$ from"node:crypto";import fc from"node:fs/promises";import Pb from"node:path";import tO from"node:os";async function It(){return Un||(zd??=fc.readFile(Yd,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(Un=t,!e&&kt()){let{records:n,source:r}=await Mt("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;Un[J(i.repo,i.number)]=i}Object.keys(Un).length&&await Xd().catch(()=>{})}}return Un}).finally(()=>zd=null),zd)}async function Xd(){let t=JSON.stringify({links:Un},null,2),e=`${Yd}.${process.pid}.tmp`;await fc.mkdir(Bb,{recursive:!0}),await fc.writeFile(e,t),await fc.rename(e,Yd)}async function Dt(t){Jd=Jd.then(Xd,Xd),await Jd.catch(()=>{});let e=Un?.[t];e&&Kt("prlinks",{id:Db(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}async function se({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 It(),c=J(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:eO.has(a),firstSeen:Date.now()},await Dt(J(t,e)),{link:i[c],created:!0})}async function Qe(t,e){if(!t||e==null)return null;let n=(await It())[J(t,e)];return!n||n.announced?null:(n.announced=!0,await Dt(J(t,e)),n)}async function Vt(t,e){return!t||e==null?null:(await It())[J(t,e)]??null}async function Hn(t,e,n){if(!t||e==null)return null;let r=(await It())[J(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 Dt(J(t,e)),r):null}async function jb(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await It())[J(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Dt(J(t,e)),o):null}async function Lb(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await It())[J(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Dt(J(t,e)),o):null}async function pc(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await It())[J(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Dt(J(t,e)),r)}async function mc(t,e){if(!t||e==null)return null;let n=(await It())[J(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Dt(J(t,e)),n)}async function hc(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await It())[J(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Dt(J(t,e)),r)}async function wc(t,e){if(!t||e==null)return null;let n=(await It())[J(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Dt(J(t,e)),n)}async function Fb(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await It())[J(t,e)];return r?(r.baseFoldAt=n,await Dt(J(t,e)),r):null}async function Ub(t,e){if(!t||e==null)return null;let n=(await It())[J(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Dt(J(t,e)),n)}async function Hb(t,e,n){if(!t||e==null||!n)return null;let r=(await It())[J(t,e)];return r?(r.lastHeadSha=n,await Dt(J(t,e)),r):null}async function Qs(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await It())[J(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Dt(J(t,e)),r):null}async function Or(t,e,n=!0){if(!t||e==null)return null;let r=(await It())[J(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Dt(J(t,e)),r):null}async function ta(t,e,n=!0){if(!t||e==null)return null;let r=(await It())[J(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Dt(J(t,e)),r):null}async function Wb(t,e,n){if(!t||e==null)return null;let r=(await It())[J(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 Dt(J(t,e)),r}async function ea(t,e){if(!t||e==null)return null;let n=(await It())[J(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 Dt(J(t,e)),n)}async function qb(){return Object.values(await It()).filter(t=>!nO.has(t.state))}async function Cr(){return Object.values(await It()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function Kb(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Mt("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await It(),r=0;for(let[o,s]of Object.entries(n))await Kt("prlinks",{id:Db(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Bb,Yd,J,Db,Un,zd,Jd,eO,nO,Wn=C(()=>{An();Bb=process.env.BOSTRAT_STATE_DIR||Pb.join(process.env.HOME||tO.tmpdir(),".bostrat"),Yd=Pb.join(Bb,"pr-links.json"),J=(t,e)=>`${t}#${Number(e)}`,Db=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=Q$.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},Un=null,zd=null,Jd=Promise.resolve();eO=new Set(["api","observed","reconciled"]);nO=new Set(["merged","closed","lost"])});function gc(t,e,n){let{state:r,reason:o}=Nu(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of na)a-c.at>=Gb()&&na.delete(i);na.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=na.get(t);return s?Date.now()-s.at>=Gb()?(na.delete(t),!1):s.busy:!1}async function Zd(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await xt(t,{lines:Nt}).catch(()=>"");if(!gc(t,e,r))return Uw(e,r)?{working:!0,prompted:!1}:n;if(Er(e,r)){let o=await Ve(t).catch(()=>r),s=Dn({name:t,agent:e},o);if(s)return!Zi({name:t,agent:e},s,o)||Py(t)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function Mr(t,e){return(await Zd(t,e)).working}var Gb,na,ra=C(()=>{Gt();ji();Ed();Id();Gb=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,na=new Map});import{execFile as rO}from"node:child_process";import{promisify as oO}from"node:util";async function Oo(t){if(!t)return null;let e=Vb.get(t);if(e&&Date.now()-e.at<aO)return e.branch;let n=null;try{let{stdout:r}=await sO("git",["-C",t,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return Vb.set(t,{at:Date.now(),branch:n}),n}function zb(t){return String(t||"").match(iO)?.[1]??null}function cO(t,e){let n=zb(t);return!!n&&!!e&&n===e}function Jb(t,e){let n=zb(t);return!!n&&n!==e}function lO(t){let e=t?.workspace;return e?.kind==="multi"?(e.repos??[]).map(n=>n?.dir).filter(Boolean):[e?.dir||t?.cwd].filter(Boolean)}async function Co(t){let e=[],n=r=>{r&&!e.includes(r)&&e.push(r)};n(t?.workspace?.branch??null);for(let r of lO(t))n(await Oo(r));return e}async function yc(t,e,n=()=>!0){if(!e)return null;let r=(t??[]).filter(a=>a&&n(a)),o=r.find(a=>a.workspace?.branch===e);if(o)return o;let s=r.find(a=>cO(e,a.id));if(s)return s;for(let a of r)if(!(a.archivedAt||a.transferredTo)&&(await Co(a)).includes(e))return a;return null}var sO,aO,Vb,iO,Mo=C(()=>{sO=oO(rO),aO=5e3,Vb=new Map;iO=/^track\/.+-([0-9a-f]{8})$/});import{execFile as uO}from"node:child_process";import{promisify as dO}from"node:util";async function Qd(t,e,{midTurn:n=Mr,pollMs:r=fO,capMs:o=pO}={}){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 ek(t){let e=String(t||""),n=0;for(let r=0;r<64;r++){Yb.lastIndex=n;let o=Yb.exec(e);if(!o)break;n=o.index+o[0].length;let s=o[1]==="-",a=o[2]??o[3]??o[4],i=e.indexOf(`
|
|
135
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+kO(a)+"[ \\t]*$","m").exec(e.slice(i+1));if(!l)continue;let u=i+1+l.index+l[0].length;e=e.slice(0,i)+e.slice(u)}return e}function tf(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function rk(t){let e=ek(t),n=e.match(gO);if(!n)return null;let r=tf(e,n.index);return bO.test(r)?null:{repo:r.match(nk)?.[1]??null,head:r.match(xO)?.[1]??null}}function ok(t){let e=ek(t),n=e.match(yO);if(!n)return null;let r=tf(e,n.index),o=tf(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("-")){SO.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(nk)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function AO(t,e,{exclude:n=()=>!1}={}){let r=async i=>{Ze("observer");let{stdout:c}=await Xb("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",TO],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Ln(l)&&Fn(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=e-mO&&!n(c)),s=await Ie({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 EO(t,e){if(e)return[e];let n=[];for(let r of sk(t)){let o=await ut(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await qe().catch(()=>[])){let o=await ut(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function sk(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 _O(t,e,n){let r=await Co(t).catch(()=>[t.workspace?.branch].filter(Boolean)),o=e?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||vO(c.headRefName,t.id),i=[];for(let c of await EO(t,e?.repo??null)){let l;try{l=await AO(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await Vt(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 ak(t,e,{delays:n=Zb,turnEnd:r}={}){let o=rk(e.command);if(!o)return null;let s=!!ok(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(qn.has(a))return null;qn.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 _O(t,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:f}=u,{link:p,created:h}=await se({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!h&&!(p?.trackId===t.id&&await Qe(d,f.number))?null:(await Qd(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{qn.delete(a)}})();return c.catch(()=>qn.delete(a)),c}function ik(t,e,{delays:n=Zb,turnEnd:r,openedWait:o}={}){let s=ok(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(qn.has(a))return null;qn.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 h=sk(t);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let y=await ut(w).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await Co(t).catch(()=>[])).filter(h=>h!==t.workspace?.branch)[0]||t.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(h,w)=>{let y=await Vt(c,h.number);return y&&y.trackId!==t.id?null:(y||await se({repo:c,number:h.number,trackId:t.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await Qs(c,h.number),w&&((await B(t).catch(()=>[])).some(A=>A.type==="pr.state.changed"&&(A.payload?.change==="auto-armed"||A.payload?.change==="merged")&&A.payload?.number===h.number)||(await Qd(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:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},p=null;for(let h of n){await new Promise(M=>setTimeout(M,h));let w=[];for(let M of d)try{Ze("observer");let{stdout:N}=await Xb("gh",["pr","view",String(l),"--repo",M,"--json",IO],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Ie({slug:M})}}),T=JSON.parse(N||"null");T?.number&&w.push({slug:M,pr:T})}catch(N){Ln(N)&&Fn(N)}let y=w.find(M=>M.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 x=await Vt(c,k.number);if(x&&x.trackId!==t.id)return null;x||await se({repo:c,number:k.number,trackId:t.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Qd(e.box,e.agent,r);let A=await B(t).catch(()=>[]);if(A.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="merged"&&M.payload?.number===k.number))return null;let R=M=>M.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="opened"&&N.payload?.number===k.number),V=0;if(!R(A)){if(rk(e.command)&&await Qe(c,k.number)){let M=Date.now();V=M+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:M,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await Vt(c,k.number))?.announced){let M=o?.capMs??hO,N=o?.pollMs??wO,T=Date.now()+M;for(;Date.now()<T&&(await new Promise(D=>setTimeout(D,N)),!R(await B(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(),V),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await en(c,k.number).catch(()=>{}),await tn(c,k.number).catch(()=>{}),await Hn(c,k.number,"merged"),k}return p?f(p,!1):null}finally{qn.delete(a)}})();return i.catch(()=>qn.delete(a)),i}var Xb,Zb,fO,pO,mO,hO,wO,Qb,tk,gO,yO,bO,Yb,kO,nk,xO,vO,SO,TO,qn,IO,ck=C(()=>{st();cr();qt();Zs();Wn();Nr();ra();Mo();Xb=dO(uO),Zb=[4e3,15e3,45e3],fO=2500,pO=5*6e4;mO=15e3,hO=9e4,wO=2e3,Qb=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,tk=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,gO=new RegExp(`${Qb}${tk}create\\b`),yO=new RegExp(`${Qb}${tk}merge\\b`),bO=/\s--web\b/,Yb=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,kO=t=>t.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");nk=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,xO=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,vO=Jb;SO=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);TO="number,url,title,headRefName,createdAt";qn=new Set;IO="number,url,title,headRefName,state"});import RO from"node:fs/promises";import nf from"node:path";import $O from"node:os";function ef(t,e){let n=new RegExp(`\\b${e}="([^"]*)"`).exec(t);return n?n[1]:null}function bc(t){let e=OO.exec(String(t||""));if(!e)return null;let[,n,r]=e,o=ef(n,"from"),s=o?o.replace(/^uds:/,""):null,a=lk(s);return{socket:s,pid:a,name:ef(n,"from-name"),mode:ef(n,"from-mode"),text:r.trim()}}function lk(t){let e=/(\d+)\.sock$/.exec(String(t||""));return e?Number(e[1]):null}function CO(){return process.env.CLAUDE_CONFIG_DIR?nf.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):nf.join($O.homedir(),".claude","sessions")}async function MO(t){let e=lk(t);if(!e)return null;try{let n=await RO.readFile(nf.join(CO(),`${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 uk(t,e){let n=await MO(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 OO,rf=C(()=>{OO=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function of(t=process.env){return!!(gt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function sf({loopId:t,outcome:e,note:n=null,env:r=process.env}={}){if(!of(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),dk);try{let a=await fk(`${gt(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 pk(t,{provider:e=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!t?.id||!String(t.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!of(s))return{ok:!1,source:"unconfigured"};let i=kc.get(t.id)||0;if(a()-i<NO)return{ok:!1,source:"debounced"};kc.set(t.id,a());let c=await xc({env:s}),l=c.loops.find(f=>f.trackId===t.id);if(!l)return kc.delete(t.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${e||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),d=await sf({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||kc.delete(t.id),{...d,loop:{id:l.id,name:l.name}}}async function xc({env:t=process.env}={}){if(!of(t))return{loops:[],source:"unconfigured"};let e=new AbortController,n=setTimeout(()=>e.abort(),dk);try{let r=await fk(`${gt(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)}}var dk,fk,NO,kc,af=C(()=>{qt();dk=1e4,fk=(...t)=>fetch(...t);NO=10*6e4,kc=new Map});import{randomUUID as PO}from"node:crypto";function oa(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function mk(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function hk(t){return`job|${t.box}|id:${t.id}`}async function lf(t){return cf.has(t.id)?cf.get(t.id):(vc.has(t.id)||vc.set(t.id,B(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent"||r.type==="provider.error")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(oa({role:o,sourceId:r.payload.sourceId})),n.keys.add(oa({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(oa({role:"user",sourceId:r.payload.sourceId})),n.keys.add(oa({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(mk({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(hk({id:r.payload.id,box:r.boxId}));return cf.set(t.id,n),n}).finally(()=>vc.delete(t.id))),vc.get(t.id))}async function wk(t,e){try{let n=(await lf(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=oa(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?bc(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 uk(s,sa),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}let a=Tw(r);if(a){await I(t,{type:"provider.error",actor:"system",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{cls:a.cls,code:a.code||void 0,status:a.status||void 0,requestId:a.requestId||void 0,text:r.text,ts:r.ts,model:r.model||null,boxRole:r.boxRole,sourceId:r.sourceId||void 0}}),pk(t,{provider:r.agent||null,cls:a.cls,status:a.status,text:r.text}).catch(()=>{});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 i=kb(r.text);if(i){let{offered:c,toPr:l}=xb(i),u;if(t.workspace?.kind==="multi"){let f=await No(t).catch(()=>[]);u=f.length?f.map(p=>({name:p.name,slug:p.repo??null})):void 0}let d=(t.workspace?.kind==="multi"?null:await Oo(t.cwd).catch(()=>null))||t.workspace.branch;await I(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:PO(),ts:r.ts,excerpt:i,branch:d,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:c||void 0,toPr:l||void 0,repos:u}})}}}}catch{}}async function gk(t,e){try{let n=await lf(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=mk(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}}),ak(t,o),ik(t,o))}}catch{}}async function yk(t,e){try{let n=await lf(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=hk(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 cf,vc,bk=C(()=>{st();Ro();ck();Mo();rf();Nr();Ci();af();cf=new Map,vc=new Map});function BO(t){let e=[],n=null;for(let r of String(t||"").split(`
|
|
136
|
-
`)){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 DO(t,e){let n=`${t.id}:${e.name}`;if(!uf.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);uf.set(n,o)}return uf.get(n)}async function kk(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await mr(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await DO(t,e),o=BO(await Zt(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:Nn(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 uf,xk=C(()=>{st();wr();ke();Vs();uf=new Map});import Sc from"node:fs";import ff from"node:path";import jO from"node:os";async function UO({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(FO,{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:vk})});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<vk)break}return r}function HO(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 Tk(){try{return JSON.parse(Sc.readFileSync(Tc(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function qO(t){try{Sc.mkdirSync(ff.dirname(Tc()),{recursive:!0});let e=Tc()+".tmp";Sc.writeFileSync(e,JSON.stringify(t)),Sc.renameSync(e,Tc())}catch{}}async function GO({fetchImpl:t,now:e=Date.now()}={}){if(Kn.rows&&e-Kn.at<WO)return Kn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Tk(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await UO({sinceMs:o,cookie:n,fetchImpl:t});Kn={at:e,rows:s.map(HO).filter(Boolean),error:null}}catch(s){Kn={at:e,rows:null,error:String(s?.message||s)}}return Kn.rows}async function VO(t,e){let n=`${t.id}:${e.name}`;if(!df.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);df.set(n,o)}return df.get(n)}async function Ak(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Sk())return;let r=await GO({fetchImpl:n});if(!r||!r.length)return;let o=await py(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await VO(t,e),a=Tk(),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=Nn(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}})}qO({sinceMs:c,seen:[...i].slice(-KO)})}catch{}}var LO,Sk,FO,vk,WO,Kn,Tc,KO,pf,df,mf=C(()=>{st();fd();Vs();LO=process.env.BOSTRAT_STATE_DIR||ff.join(process.env.HOME||jO.tmpdir(),".bostrat"),Sk=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,FO="https://cursor.com/api/dashboard/get-filtered-usage-events",vk=100;WO=5*6e4,Kn={at:0,rows:null,error:null},Tc=()=>ff.join(LO,"cursor-usage.json");KO=5e3;pf=()=>({enabled:Sk(),lastPollAt:Kn.at||null,error:Kn.error}),df=new Map});import ca from"node:fs/promises";import ee from"node:path";import zO from"node:os";import JO from"node:net";import{randomUUID as YO}from"node:crypto";import{execFile as XO}from"node:child_process";import{promisify as ZO}from"node:util";function sC(t){for(let e of xf(t))if(!Ok.test(e))return e;return null}function Ck(t){let e=xf(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function aC(t,e){let n=Ck(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return ee.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return ee.resolve(t,n[r+1])}return t}function cC(t){let e=!1,n=!1;for(let r of xf(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&&hf(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,hf(r))return`serves the filesystem root ("${r}")`;continue}if(hf(r))return`serves the filesystem root ("${r}")`;if(iC(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function uC(t,e){if(ee.resolve(t)===ee.resolve(e))return!0;let[n,r]=await Promise.all([Ac(t),Ac(e)]);return n===r}async function dC(t){let n=(await ca.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 Mk(ee.join(t,o.name,yf))?`./${o.name}`:null))).filter(Boolean)}async function fC(t,e){let[n,r]=await Promise.all([Ac(t),Ac(e)]),o=ee.relative(n,r);if(o&&(o.startsWith("..")||ee.isAbsolute(o)))throw aa(`serve directory escapes the Track workspace: ${e}`)}async function pC(t,e){let n=cC(e);if(n)throw aa(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await lC(t))throw aa(`serve directory does not exist: ${t}`);let r=sC(e);if(!r)return;if(!r.includes("/")&&!await Zh(r,{loginShell:!0,failOpen:!0}))throw aa(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(nC.test(e)||rC.has(r)||!eC.has(r)||!Ck(e).includes("run")||oC.test(e))return;let o=aC(t,e);if(await Mk(ee.join(o,yf)))return;let s=await dC(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw aa(`no ${yf} in ${o}${a}`)}async function on(){return Ce||(wf||=(async()=>{try{Ce=JSON.parse(await ca.readFile($k,"utf8"))}catch{Ce={views:[],adoptWatermarks:{}}}finally{wf=null}return Ce.views||=[],Ce.adoptWatermarks||={},Ce})(),wf)}async function Ne(){await Xt($k,Ce)}function rn(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 Sf(t){return ia.get(t)||null}function Pk(t,{name:e,box:n}={}){let r=ia.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 ia.set(t,o),o}function bf(t,e){let n=ia.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ec(t,{error:e=null}={}){let n=ia.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Bk(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 kf(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function mC(t,e){let n=kf(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Ik.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Ik.has(s))return s}}function la(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=JO.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 Tf(t){if(process.platform!=="darwin"){await Me("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Me("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${t}`]))}catch{return}for(let n of e.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function Dk(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function hC(t,e){let n=new Set(t.views.map(o=>o.port)),r=Dk();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=Ek;o<=_k;o++)if(!(n.has(o)||r.has(o))&&!await la(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Ek}-${_k}`)}async function wC(t){try{let{stdout:e}=await Me(L,H("list-sessions","-F","#{session_name}"));return e.split(`
|
|
137
|
-
`).
|
|
138
|
-
`).
|
|
139
|
-
|
|
140
|
-
`)}catch{}}function Vk(){let t={};for(let[e,n]of Rf)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:Rc.slice(-20)}}var Gk,vC,SC,Rc,Rf,Oc=C(()=>{Gk=process.env.BOSTRAT_STATE_DIR||Kk.join(process.env.HOME||xC.tmpdir(),".bostrat"),vC=Kk.join(Gk,"github-latency.ndjson"),SC=100,Rc=[],Rf=new Map});import{execFile as AC}from"node:child_process";import{promisify as EC}from"node:util";async function _C(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([Do("git",["-C",t,"rev-parse","HEAD"]),Do("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 fe(t,e,n={}){let{caller:r="prs",...o}=n,s=await Ie({cwd:t});Ze(r);try{let{stdout:a}=await Do("gh",e,{cwd:t,...IC,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Ln(a)&&Fn(a),a}}async function Cf(){if(Cc&&Date.now()-Cc.at<6e4)return Cc.data;let t;try{Ze("auth");let e=await Do("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 vs();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Cc={at:Date.now(),data:t},t}async function RC(t){try{let{stdout:e}=await Do("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function Jk(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 $f(t){let e=(t||[]).map(Jk),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 Nf(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Xk(t,e){if(!e)return null;let n=await ut(t);if(!n)return null;try{let r=await fe(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 Zk(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 Qk(t,e){let n=await Xk(t,e);return n===null?{...$f([]),unavailable:!0}:$f(Zk(n))}function Mc(t,e=$f(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 OC(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)<$C}for(let o of Bo.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Bo.delete(o);return e}async function jo(t,e){let n=await sn(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await fe(n.path,["pr","view",String(r),"--json",Mf]);return Mc(JSON.parse(o))}catch(o){if(!Nf(o))throw o;let s=await fe(n.path,["pr","view",String(r),"--json",Yk]),a=JSON.parse(s);return Mc(a,await Qk(n.path,a.headRefOid))}}async function Pr(t,{caller:e="prs"}={}){try{let n=await fe(t,["pr","list","--json",Mf,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>Mc(r)).sort(zk)}catch(n){if(!Nf(n))throw n;let r=await fe(t,["pr","list","--json",Yk,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Mc(o,await Qk(t,o.headRefOid))))).sort(zk)}}function Be(){da.clear()}async function NC(t,e){if(!t)return e;for(let n of e){let r=await Vt(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 pc(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await hc(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function tx(){let t=await Cf();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await qe())await RC(a.path)&&e.push(a);let n=Date.now(),r=dc(n)>0,o=await Promise.all(e.map(async a=>{let i=da.get(a.path),c=i?.pending?MC:CC;if(i&&(r||n-i.at<c))return i.entry;try{let l=await ut(a.path).catch(()=>null),u=OC(a.path,await Pr(a.path,{caller:"tab"}),n);if(await NC(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&&$c({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 da.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:Pe(l)}}})),s=new Set(e.map(a=>a.path));for(let a of da.keys())s.has(a)||da.delete(a);return{auth:t,repos:o}}async function sn(t){let e=(await qe()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function Br(t){return Zr(t)}async function Lo(t,e,{method:n="squash"}={}){let r=await sn(t);if(!fa.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 Ie({cwd:r.path})).GH_TOKEN){let a=await zl().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await fe(r.path,s)}catch(a){throw new Error(Pe(a))}finally{Be()}return{ok:!0,number:o}}async function pa(t,e){let n=await sn(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await fe(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Pe(s))}finally{Be()}let o=await ut(n.path).catch(()=>null);return o&&await Fb(o,r).catch(()=>{}),{ok:!0,number:r}}async function Pf(t,e){let n=await sn(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await fe(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Pe(o))}finally{Be()}return{ok:!0,number:r}}function Bf(t){return t.green&&!t.isDraft}function jC(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 ex(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:Bf(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function nx(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}function rx(){let t={};for(let[e,n]of Of)t[e]={...n};return t}async function ox(t,{method:e="merge",onMerged:n=null}={}){let r=await sn(t);if(!fa.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 Pr(r.path)).filter(Bf).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),FC(s,o,{listPrs:()=>Pr(r.path),merge:a=>Lo(r.path,a,{method:e}),sleep:DC,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function LC(t,e){let n=Date.now()+(e.settleMaxMs??BC),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(PC)}}async function FC(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 LC(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: ${Pe(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(!Bf(a)){t.skipped.push({number:a.number,title:a.title,reason:jC(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:Pe(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Be()}}async function Df(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Do("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Pe(l)}`)}await _C(t);let o=n||"";if((await Ie({cwd:t})).GH_TOKEN){let l=await zl().catch(()=>null);l&&(o=`${o?`${o}
|
|
141
|
-
|
|
142
|
-
`:""}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 fe(t,s)}catch(l){throw new Error(Pe(l))}finally{Be()}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 sx(t,e){let n=await sn(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await fe(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Pe(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Nf(i))throw i;let c=await fe(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Xk(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Pe(i));o=Zk(l).map(Jk).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=UC(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await fe(n.path,["run","view",c,"--log-failed"]).then(u=>HC(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function UC(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function HC(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
|
|
143
|
-
`);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
|
|
144
|
-
`+o.slice(-e).join(`
|
|
106
|
+
`});function XI(e){return{id:e.id,kind:"prompt",groupId:e.groupId,provider:e.provider,box:e.box,agent:e.provider,questionTotal:e.questions.length,answeredVia:"chat",questions:e.questions.map((t,n)=>({index:n,header:t.header||"",question:t.question,multiSelect:!!t.multiSelect,chosen:[]})),ts:e.ts}}function QI(e,t,n){let r=Sg(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){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?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.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 Er(e){let t=[],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 e||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let p=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(p))for(let S of p.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let $=S[0].split("/").pop().toLowerCase();f.has($)||f.set($,[]),f.get($).push(g.payload?.ts??g.ts)}}let d=3*6e4,m=20*6e4,h=(g,p)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some($=>$>=p-d&&$<=p+m);for(let g of e||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let p=Ne(g.payload?.text);p&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(p))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let p=Ne(g.payload?.text);p&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(p))}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 p=g.payload.note,S={status:p.status||null,summary:p.summary||null,resultHead:p.resultHead??null,ts:se(g)};p.toolUseId&&i.set(p.toolUseId,S);let $=String(p.summary||"").match(u);$&&l.set($[1],S)}let w=new Map,y=new Map,b=null,x=new Map,E=new Map,_=new Map,q=new Map,C=new Map,N=new Map,T=new Map,W=new Map,H=new Map,ae=new Map,J=new Map,A=new Map,j=new Map,Q=()=>{for(let g of j.values())g.questions.length&&t.push(g);j.clear()},fe=null,Re=6e4,ue=new Map,ie=(g,p)=>{let S=o.get(g);if(!S?.length)return!1;let $=Ne(p),we=s.get(g);return S.some(ge=>{if(!$.startsWith(ge))return!1;let Se=Ne($.slice(ge.length));return!!Se&&!!we?.has(Se)})};for(let g of e||[]){let p=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(p.text||"").trim()&&!p.images?.length&&!p.attachments?.length)break;{let S=p.note||Zi(p.text);if(S){if(S.kind==="task"){if(S.toolUseId&&a.has(S.toolUseId))break;let $=String(S.summary||"").match(u);if(!S.toolUseId&&$&&c.has($[1]))break}if(S.kind==="command"&&wg(S.command))break;if(S.kind==="resume-summary"){t.push({id:g.id,kind:"resume-summary",title:S.title||"Context resumed from summary",bullets:S.bullets||[],sections:S.sections||[],rawChars:S.rawChars??null,ts:se(g),box:g.boxId,agent:g.provider});break}S.kind!=="suppress"&&t.push({id:g.id,kind:"event",type:yg(S),name:S.name??null,command:S.command??null,summary:S.summary??null,status:S.status??null,ts:se(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${se(g)}`)||g.source==="transcript"&&ie(g.boxId,p.text))break;if(p.kind==="merge-notify"||p.kind==="ship-notify"){t.push({id:g.id,kind:"event",type:p.kind==="ship-notify"?"ship-notified":"merge-notified",text:p.text,ts:se(g),box:g.boxId,agent:g.provider});break}if(p.kind==="mock-picked"||p.kind==="ci-fix"||p.kind==="reconcile"||p.kind==="pr-go"||p.kind==="push-go"||p.kind==="spin-up-view"||p.kind==="prompt-answer"){p.kind==="prompt-answer"&&(fe=null);break}for(let S of x.values())S.status="stale";x.clear();for(let S of _.values())S.status="stale";_.clear(),fe&&fe.box===g.boxId&&(t.push(XI(fe)),fe=null),t.push({id:g.id,kind:"message",role:"user",text:p.text,ts:se(g),box:g.boxId,agent:g.provider,boxRole:p.boxRole,...g.actor==="orchestrator"?{orchestration:p.kind||!0}:{},...p.sourceId?{sourceId:p.sourceId}:{},...p.attachments?.length?{attachments:p.attachments}:{},...p.images?.length?{images:p.images}:{}});break}case"peer.message":{let S=p.from||{};t.push({id:g.id,kind:"event",type:"peer-message",text:p.text,from:S.trackName||S.boxId||S.name||null,fromTrackId:S.trackId||null,ts:se(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(p.model==="<synthetic>"&&/^No response requested\.$/.test(String(p.text||"").trim()))break;let S=Mw(p,{codexFailedText:vr});if(S){t.push({id:g.id,kind:"event",type:"provider-error",cls:S.cls,status:S.status??void 0,text:S.text,provider:g.provider,legacy:!0,at:se(g),ts:se(g),box:g.boxId,agent:g.provider});break}Q();let $=(p.images||[]).filter($e=>!h($e,se(g)));if(!String(p.text||"").trim()&&!$.length)break;if(g.provider==="cursor"){let $e=`${g.boxId}|${p.text}`;if(n.has($e))break;n.add($e)}(!fe||fe.box===g.boxId)&&(fe=null);let we=g.provider==="codex"?(p.tools||[]).filter($e=>!Mu.has($e)&&!Hs.has($e)):p.tools||[],ge=p.text,Se=null;if(td.has(g.provider)){let $e=nd(String(p.text||""));$e&&(ge=$e.text,Se={questions:$e.questions})}t.push({id:g.id,kind:"message",role:"assistant",text:ge,ts:se(g),box:g.boxId,agent:g.provider,boxRole:p.boxRole,...Se?{ask:Se}:{},...we.length?{tools:we}:{},...p.model?{model:p.model}:{},...p.sourceId?{sourceId:p.sourceId}:{},...$.length?{images:$}:{}}),Se&&(fe={id:g.id,groupId:rd(g.provider,p.sourceId||g.id,Se.questions),provider:g.provider,box:g.boxId,questions:Se.questions,ts:se(g)});break}case"job.started":{if(!p.id)break;let S=se(g),$=i.get(p.id);if(!$&&(p.kind||"agent")==="agent"&&p.name){let v=l.get(p.name);v&&v.ts>=S&&($=v)}let we={jobId:p.id,jobKind:p.kind||"agent",name:p.name||null,detail:p.detail||null,status:$?$.status==="killed"?"stopped":$.status&&$.status!=="completed"?"failed":"done":"running",startedTs:S,finishedTs:$?.ts??null,summary:$?.summary??null,resultHead:$?.resultHead??null},ge=ue.get(g.boxId),Se=ge?.jobs[ge.jobs.length-1]?.startedTs;if(ge&&Se!=null&&S-Se<=Re)ge.jobs.push(we);else{let v={id:g.id,kind:"job-dock",dockId:p.id,ts:S,box:g.boxId,agent:g.provider,jobs:[we]};ue.set(g.boxId,v),t.push(v)}break}case"handoff.done":t.push(pt(g,"lead-handoff"));break;case"subtask.delegated":t.push({...pt(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":t.push(pt(g,"subtask-merged"));break;case"pr.state.changed":{if(!p.change)break;if(p.change==="opened"){for(let $ of x.values())$.status="stale";x.clear();for(let $ of _.values())$.status="stale";_.clear()}let S=pt(g,`pr-${p.change}`);if(S.box=g.boxId??null,ZI.has(p.change)&&p.number!=null){let $=QI(w,y,p);if(w.size>=2){for(let ge of y.values())for(let Se of ge)Se.grouped=!0;S.groupPrs=[...w.values()];let we=[S];for(let ge of w.keys())y.set(ge,we)}else{let we=y.get($)??[];we.push(S),y.set($,we)}}else if(p.change==="merged"||p.change==="closed"||p.change==="lost"){let $=Sg(p),we=b&&!b.complete||w.size>=2;if(w.has($))w.delete($),y.delete($);else if(p.number!=null)for(let ge of[...w.keys()])ge.endsWith(`#${p.number}`)&&(w.delete(ge),y.delete(ge));if(we&&p.number!=null){(!b||b.complete)&&(b={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:se(g),ts:se(g),box:g.boxId,agent:g.provider},t.push(b)),b.prs.push({number:p.number,url:p.url??null,title:p.title??null,repo:p.repo??null,branch:p.branch??null,change:p.change,ts:se(g)}),b.remaining=[...w.values()],b.complete=w.size===0;break}}t.push(S);break}case"pr.offered":{if(!p.offerId)break;for(let $ of x.values())$.status="stale";x.clear();for(let $ of _.values())$.status="stale";_.clear();let S={id:g.id,kind:"pr-offer",offerId:p.offerId,excerpt:p.excerpt||null,branch:p.branch||null,base:p.base||null,offered:Array.isArray(p.offered)&&p.offered.length?p.offered:null,toPr:p.toPr&&typeof p.toPr=="object"?{number:p.toPr.number??null}:null,available:Array.isArray(p.available)?p.available:null,defaults:Array.isArray(p.defaults)?p.defaults:null,prState:p.prState||null,openPr:p.openPr&&typeof p.openPr=="object"?p.openPr:null,repos:Array.isArray(p.repos)&&p.repos.length?p.repos:null,status:"open",ts:se(g),box:g.boxId,agent:g.provider};x.set(p.offerId,S),E.set(p.offerId,S),t.push(S);break}case"pr.offer.answered":{let S=E.get(p.offerId);S&&(S.status=p.action||"dismiss",S.actions=Array.isArray(p.actions)?p.actions:null,S.notify=p.notify===!0,p.existingPr&&typeof p.existingPr=="object"&&(S.existingPr={number:p.existingPr.number??null}),x.delete(p.offerId));break}case"pr.exists.offered":{if(!p.offerId)break;for(let $ of _.values())$.status="stale";_.clear();let S={id:g.id,kind:"pr-exists-offer",offerId:p.offerId,srcOfferId:p.srcOfferId??null,branch:p.branch||null,base:p.base||null,pr:p.pr&&typeof p.pr=="object"?{number:p.pr.number??null,url:p.pr.url??null,title:p.pr.title??null}:null,actions:Array.isArray(p.actions)&&p.actions.length?p.actions:null,notify:p.notify===!0,status:"open",ts:se(g),box:g.boxId,agent:g.provider};_.set(p.offerId,S),q.set(p.offerId,S),t.push(S);break}case"pr.exists.answered":{let S=q.get(p.offerId);S&&(S.status=p.choice||"dismiss",_.delete(p.offerId));break}case"box.state.changed":{let S=p.change==="model"?"model-change":p.change;if(!S)break;let $=pt(g,S);S==="model-change"&&($.to=p.model),t.push($);break}case"track.transferred":case"track.recovered":t.push(pt(g,"track-moved"));break;case"view.state.changed":{if(p.change!=="created"||p.restarted)break;t.push({id:g.id,kind:"view-card",viewId:p.viewId,name:p.name,slug:p.slug,port:p.port,origin:p.origin,ts:se(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{t.push({id:g.id,kind:"view-spinup-failed",name:p.name||null,reason:p.reason||"no-view",ts:se(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(p.change==="picked"){t.push({id:g.id,kind:"mock-picked",mockId:p.mockId,name:p.name,group:p.group??null,version:p.version??null,siblings:p.siblings??[],ts:se(g),box:g.boxId,agent:g.provider});break}if(p.change!=="created")break;t.push({id:g.id,kind:"mock-card",mockId:p.mockId,name:p.name,group:p.group??null,ts:se(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(p.change!=="created")break;t.push({id:g.id,kind:"flow-card",flowId:p.flowId,name:p.name,stepCount:p.stepCount??null,mockIds:p.mockIds??[],ts:se(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(p.change!=="created")break;t.push({id:g.id,kind:"runbook-card",runbookId:p.runbookId,name:p.name,ts:se(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{t.push({id:g.id,kind:"ticket-card",provider:p.provider||"linear",key:p.key,url:p.url,title:p.title??null,linkSource:p.linkSource??null,ts:se(g),box:g.boxId,agent:g.provider});break}case"box.fullscreen.detected":{if(!p.offerId)break;for(let $ of T.values())$.box===(p.box||g.boxId)&&($.status="stale",T.delete($.offerId));let S={id:g.id,kind:"fullscreen-offer",offerId:p.offerId,status:"open",ts:se(g),box:p.box||g.boxId,agent:p.agent||g.provider};T.set(p.offerId,S),W.set(p.offerId,S),t.push(S);break}case"box.fullscreen.answered":{let S=W.get(p.offerId);S&&(S.status=p.action==="restarted"?"restarted":p.action==="cleared"?"cleared":"dismiss",T.delete(p.offerId));break}case"ticket.move.offered":{if(!p.offerId)break;for(let $ of C.values())$.key===p.key&&($.status="stale",C.delete($.offerId));let S={id:g.id,kind:"ticket-offer",offerId:p.offerId,provider:p.provider||"linear",key:p.key,url:p.url??null,phase:p.phase??null,from:p.from??null,to:p.to?.name??null,prNumber:p.prNumber??null,status:"open",ts:se(g),box:g.boxId,agent:g.provider};C.set(p.offerId,S),N.set(p.offerId,S),t.push(S);break}case"ticket.move.answered":{let S=N.get(p.offerId);S&&(S.status=p.action==="moved"?"moved":"dismiss",C.delete(p.offerId));break}case"ticket.state.changed":{for(let S of C.values())S.key===p.key&&S.to===p.to&&(S.status="stale",C.delete(S.offerId));if(p.mode==="offer")break;t.push(pt(g,"ticket-state"));break}case"ticket.commented":{t.push({id:g.id,kind:"ticket-comment",key:p.key,url:p.url??null,excerpt:p.excerpt??null,ts:se(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":t.push(pt(g,"ticket-flow"));break;case"ticket.create.offered":{if(!p.proposalId)break;for(let $ of H.values())$.status="stale";H.clear();let S={id:g.id,kind:"ticket-create-offer",proposalId:p.proposalId,provider:p.provider||"linear",epic:p.epic??null,issues:Array.isArray(p.issues)?p.issues:[],team:p.team??null,needsTeam:!!p.needsTeam,linkCurrentTrack:!!p.linkCurrentTrack,status:"open",ts:se(g),box:g.boxId,agent:g.provider};H.set(p.proposalId,S),ae.set(p.proposalId,S),t.push(S);break}case"ticket.create.answered":{let S=ae.get(p.proposalId);S&&(S.status=p.action==="created"?"created":"dismiss",S.createdCount=p.createdCount??null,S.failedCount=p.failedCount??null,H.delete(p.proposalId));break}case"loop.create.offered":{if(!p.proposalId)break;for(let $ of J.values())$.status="stale";J.clear();let S={id:g.id,kind:"loop-create-offer",proposalId:p.proposalId,name:p.name??"",schedule:p.schedule??"",prompt:p.prompt??"",expectation:p.expectation??null,notify:p.notify??"off",assignment:p.assignment??{provider:"claude"},repo:p.repo??null,status:"open",ts:se(g),box:g.boxId,agent:g.provider};J.set(p.proposalId,S),A.set(p.proposalId,S),t.push(S);break}case"loop.create.answered":{let S=A.get(p.proposalId);S&&(S.status=p.action==="created"?"created":"dismiss",J.delete(p.proposalId));break}case"loop.created":{let S=p.proposalId?A.get(p.proposalId):null;S&&(S.created={loopId:p.loopId??null,nextFireAt:Number.isFinite(Number(p.nextFireAt))?Number(p.nextFireAt):null});break}case"ticket.created":{let S={items:Array.isArray(p.items)?p.items:[],failed:Array.isArray(p.failed)?p.failed:[],team:p.team??null},$=p.proposalId?ae.get(p.proposalId):null;if($){$.created=S;break}t.push({id:g.id,kind:"ticket-created",...S,ts:se(g),box:g.boxId,agent:g.provider});break}case"error":{if(!p.kind)break;t.push(pt(g,p.kind));break}case"provider.error":t.push({...pt(g,"provider-error"),provider:g.provider,box:g.boxId,agent:g.provider});break;case"usage.cap.hit":t.push(pt(g,"usage-cap-hit"));break;case"usage.cap.warning":t.push(pt(g,"usage-cap-warning"));break;case"provider.connected":{t.push({id:g.id,kind:"event",type:"provider-connected",provider:p.provider||g.provider,ts:se(g),at:se(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(fe=null,p.control||!p.question)break;let S=p.groupId||p.promptId;if(!S)break;let $=j.get(S);$||($={id:g.id,kind:"prompt",groupId:S,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:p.questionTotal??1,questions:[],ts:se(g)},j.set(S,$));let we=p.questionIndex??0,ge={index:we,header:p.header||"",question:p.question,multiSelect:!!p.multiSelect,chosen:Array.isArray(p.chosen)?p.chosen:[]},Se=$.questions.findIndex(v=>v.index===we&&Ne(v.question)===Ne(ge.question));Se>=0?$.questions[Se]=ge:$.questions.push(ge),$.questions.sort((v,V)=>v.index-V.index),$.questionTotal=Math.max($.questionTotal,p.questionTotal??1),$.questions.length>=$.questionTotal&&(t.push($),j.delete(S));break}default:break}}return tR(t.sort((g,p)=>(g.ts??0)-(p.ts??0)))}function tR(e){let t=new Map,n=[];for(let r of e){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=t.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=eR){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};t.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else t.delete(r.box),n.push(r)}return n}function ad(e,t=null){let n=[];for(let o of e||[]){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:nR(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(t??Er(e)).filter(o=>o.kind==="message");return Aw(n,r)}function nR(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==go(e.command)?t:Pn(e.command,e.description||"")}function pt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:se(e),ts:se(e)}}function Tg(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+rR,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&oR.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Ag(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!aR.has(r.type)||!r.box)continue;let o=(r.ts??0)+sR,s=n;for(;s+1<t.length;){let a=t[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&&t.splice(s,0,...t.splice(n,1)),n=s}return t}var se,ZI,Sg,eR,rR,oR,sR,aR,id=O(()=>{yo();Sr();Fi();Ju();ed();sd();se=e=>e.payload?.ts??e.ts;ZI=new Set(["opened","ci-fixed","reconciled","pushed"]),Sg=e=>`${e.repo??""}#${e.number}`;eR=2*6e4;rR=12e4,oR=new Set(["archived","unarchived","opened"]);sR=2500,aR=new Set(["pr-opened","pr-merged","pr-auto-armed"])});function dR(e){let t=null,n=null,r=null;for(let o of e??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(t={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:t,lastPush:n,lastEventAt:r}}function pR(e){for(let t=(e?.length??0)-1;t>=0;t--){let n=e[t];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function mR(e,t){let n=[],r=[],o=new Set,s=new Set;for(let a=(e?.length??0)-1;a>=0;a--){let i=e[a];if(i?.type!=="tool.called")continue;if(t!=null&&(i.ts??0)<=t)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<lR&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<uR&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Rg(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=dR(e),s=pR(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Er(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-iR);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Ig[r]??Ig.failover),l.push(""),o.lastPush?.sha){let d=_g(n,o.lastPush.ts),m=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${m}`+(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=_g(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=mR(e??[],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"}:** ${fR(d.text,cR)}`),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 iR,cR,Eg,lR,uR,fR,_g,Ig,$g=O(()=>{id();iR=12,cR=2e3,Eg=48e3,lR=30,uR=10;fR=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},_g=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Ig={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 hR from"node:fs";import jn from"node:fs/promises";import Ln from"node:path";function Dg(e){let t=yr(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=Og?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,Og).toString("utf8").replace(/�+$/,"")}
|
|
110
|
+
|
|
111
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function TR(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(Mg.test(s)){if(r.length>=Wg)continue;let a=SR(s.replace(Mg,"").replace(/[.;,:]+$/,""),qg,xR);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>Cg&&(o=`${o.slice(0,Cg-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Kg(e,t,{timeoutMs:n=9e4}={}){let r=Ln.join(t,bR),o=Ln.join(r,kR);await jn.mkdir(r,{recursive:!0});let s=await Qi(o),a=await oe(e);if(!a)throw new Error(`session "${e}" not found`);return await ec(e,a,vR(o)),await Zg(o,s,n),TR(await jn.readFile(o,"utf8"))}function Gg(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===Lg||n===Hg;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(jg)||n===wR||r.startsWith(Fg)||r.startsWith(Ug)}function Vg(e){let t=[];for(let r of e?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-AR;t.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&t.push({box:r.to,from:s,to:o})}let n=e?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&t.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&t.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),t}function zg(e,t){let n=[];for(let r of e?.boxes||[]){let o=t?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function Jg(e,{windows:t=[]}={}){if(Gg(e.role,e.text))return!0;if(e.role!=="assistant"||!e.ts)return!1;for(let n of t)if(e.box===n.box&&e.ts>=n.from&&e.ts<=n.to)return!0;return!1}function tc(e){return cd.get(e)||null}async function Yg(e){let t=cd.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await oe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:Ln.join(n.cwd,Pg,Bg)};return cd.set(e,r),IR(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function IR(e,t){e.step="summarize",await Xg(e.name,t),e.step="clear";let n=await oe(e.name);await ec(e.name,n,"/clear"),await dd(ER),e.step="resume",await ec(e.name,n,yR),e.step="done",e.finishedAt=Date.now()}async function Xg(e,t,{timeoutMs:n=_R}={}){let r=Ln.join(t,Pg),o=Ln.join(r,Bg);await jn.mkdir(r,{recursive:!0});let s=await Qi(o),a=await oe(e);await z({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await ec(e,a,gR(o)),await Zg(o,s,n),await z({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Qi(o)});let i=await og(t,o,{exclude:u=>Gg(u.role,u.text)});await z({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await I({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await jn.readFile(o,"utf8");await I({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Dg(u),ts:Date.now()}})}catch{}}await Hh(t);let l=await RR(r,o);return c&&await Qh(c,l||o,{box:e,agent:a?.agent??null}),o}async function ld(e,{timeoutMs:t}={}){let n=await oe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return Xg(e,n.cwd,t?{timeoutMs:t}:void 0)}async function ud(e,t,{trackName:n="this Track"}={}){let r=await B({id:e,cwd:t}),o=Rg(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Ln.join(t,Ng);return await jn.mkdir(Ln.dirname(s),{recursive:!0}),await jn.writeFile(s,o,"utf8"),await I({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Dg(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Ng}async function Qi(e){try{return(await jn.stat(e)).mtimeMs}catch{return 0}}async function Zg(e,t,n){let r=Date.now()+n;for(;;){if(await Qi(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await dd(2e3)}}async function RR(e,t){try{if(!hR.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Ln.join(e,`${n}.md`);return await jn.copyFile(t,r),r}catch{return null}}function dd(e){return new Promise(t=>setTimeout(t,e))}async function ec(e,t,n){if(t){let o=t.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":t.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":t.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await Dn({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await oe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Oe(e,["Escape"],{kind:"drive-wipe"}),await dd(150),await po(e,Di,{kind:"drive-wipe"}),await je(e,n,{kind:"orchestration-drive",...Mt(r.agent)})}var Pg,Bg,Og,jg,Lg,Fg,wR,gR,yR,Ug,Hg,bR,kR,Cg,Wg,qg,xR,vR,Mg,SR,AR,ER,_R,cd,Ng,fd=O(()=>{ze();Gi();Yt();zs();sg();Ti();le();Ri();$g();Pg=".bostrat/handoff",Bg="latest.md",Og=32*1024;jg="Checkpoint: write a work-in-progress handoff",Lg="HANDOFF WRITTEN",Fg="Resume from .bostrat/handoff/latest.md",wR="/clear",gR=e=>`${jg} to the EXACT absolute path ${e} (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 ${Lg} and take no further action.`,yR=`${Fg}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Ug="Beat: write a",Hg="BEAT WRITTEN",bR=".bostrat/arc",kR="beat.txt",Cg=200,Wg=4,qg=5,xR=36,vR=e=>`${Ug} short summary of what this workstream accomplished, to the EXACT absolute path ${e} (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 ${Wg} further lines, each starting with "- " and naming ONE piece in at most ${qg} 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 ${Hg} and take no further action.`,Mg=/^(?:[-–—*•·]|\d+[.)])\s+/,SR=(e,t,n)=>{let r=e.split(/\s+/).filter(Boolean).slice(0,t).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};AR=6e5;ER=3500,_R=3e5,cd=new Map;Ng=".bostrat/handoff/status-latest.md"});function ey(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!$R.test(String(e)))throw new Error(`pricing.js: ${n} window ${t} must be YYYY-MM-DD, got ${JSON.stringify(e)}`);let r=Date.parse(`${e}T${t==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${t} is not a real date: ${e}`);return r}function OR(e){let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=r.map(o=>({...o,fromMs:ey(o.from,"from",n),toMs:ey(o.to,"to",n)}))}return t}function MR(e){if(e==null)return Date.now();if(typeof e=="number")return Number.isFinite(e)?e:null;if(e instanceof Date){let n=e.getTime();return Number.isFinite(n)?n:null}let t=Date.parse(String(e));return Number.isFinite(t)?t:null}function NR(e){if(!e)return null;let t=String(e).trim();return pd[t]?t:Qg[t]?Qg[t]:Object.keys(pd).find(r=>t.startsWith(r))||null}function ty(e,t){let n=NR(e);if(!n)return null;let r=MR(t);return r===null?null:CR[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function Fn(e,t={},n){let r=ty(e,n);return r?((t.input||0)*r.input+(t.output||0)*r.output+(t.cacheWrite||0)*r.cacheWrite+(t.cacheRead||0)*r.cacheRead)/1e6:0}var pd,Qg,$R,CR,ny,ea=O(()=>{pd={"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)."}]},Qg={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"},$R=/^\d{4}-\d{2}-\d{2}$/;CR=OR(pd);ny=(e,t)=>ty(e,t)!==null});function PR(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function oy(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Qt(e){let t=e.payload?.model||"unknown",n=PR(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.source==="vendor",s=r,a=!1;if(n&&!o){let l=Fn(t,n,e.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&&!ny(t,e.ts);return{usd:s,model:t,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function Un(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=ry();for(let d of n){let{usd:m,model:h,tokens:w,repriced:y,unpriced:b,asRecorded:x}=Qt(d);i+=x;let E=d.provider||"unknown",_=d.boxId||"unattributed";if(y&&c++,b&&(l++,u.add(h)),a+=m,oy(f,w),r[h]||={usd:0,tokens:ry(),events:0},r[h].usd+=m,r[h].events+=1,oy(r[h].tokens,w),o[E]||={usd:0,events:0},o[E].usd+=m,o[E].events+=1,s[_]||={usd:0,events:0,provider:E,models:new Set,label:t[_]||_},s[_].usd+=m,s[_].events+=1,h!=="unknown"&&s[_].models.add(h),d.epoch!=null){let q=(s[_].epochs||={})[d.epoch]||={usd:0,events:0,provider:E,models:new Set};q.usd+=m,q.events+=1,q.provider=E,h!=="unknown"&&q.models.add(h)}}return{totalUsd:vo(a),asRecordedUsd:vo(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:nc(r,d=>({...d,usd:vo(d.usd)})),byProvider:nc(o,d=>({...d,usd:vo(d.usd)})),byBox:nc(s,d=>({usd:vo(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:nc(d.epochs,m=>({usd:vo(m.usd),events:m.events,provider:m.provider,models:[...m.models]}))}:{}}))}}var ry,vo,nc,_r=O(()=>{ea();ry=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});vo=e=>Math.round(e*1e4)/1e4,nc=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function BR(e){if(!Number.isFinite(e)||e<=0)return null;let t=e/864e5;if(t>=1)return`${Math.round(t)}d`;let n=e/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(e/6e4))}m`}function sy(e,t=[]){let n=[],r=e?.events||[],o=new Map,s=0;for(let u of t){if(u.type!=="pr.state.changed")continue;let f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,m]=f.split("#");return`${d.split("/").pop()}#${m}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(md(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(md(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(`${md(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Un(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?BR(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var md,ay=O(()=>{_r();md=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as DR}from"node:child_process";import{promisify as jR}from"node:util";import{randomUUID as LR}from"node:crypto";async function hd(e,t){let n=Date.now()+t;for(;;){if(await fo(e))return!0;if(Date.now()>n)return!1;await rc(UR)}}async function WR(e,t){let n=cy[t]||cy.default;n.escape&&(await Oe(e,["Escape"],{kind:"swap-exit"}),await rc(150)),n.wipe&&await po(e,Di,{kind:"swap-exit"}),await ji(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Mt(t).submitDelayMs||0;await rc(Math.max(r,250)),await Oe(e,["Enter"],{kind:"swap-exit"})}async function qR(e,t){try{let{stdout:n}=await So(L,U("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ly(e){await So(L,U("respawn-pane","-k","-t",e))}async function wd(e,t={}){if(!Ee(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await oe(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||Cn(n),a=Number(t.exitTimeoutMs)||FR;await z({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await fo(e)&&(await WR(e,o),!await hd(e,a)&&(i=!0,await z({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await ly(e),!await hd(e,iy))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&br(n),l=c?mi():null,u=await qR(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await So(L,U("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await So(L,U("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?Oi(y,l):y}if(f&&!i&&(await ly(e),!await hd(e,iy)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,m=t.resumeSessionId||LR(),h=d?n:bu(n,m,e),w=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await En(r.cwd),s==="codex"&&await to(r.cwd),s==="cursor"&&await no(r.cwd),await kr(e),await So(L,U("set-option","-t",e,"@bostrat-sid",m)),await So(L,U("set-option","-t",e,"@bostrat-agent",s)),await rc(HR),await je(e,h,{kind:"swap-launch"}),await z({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:m,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:m,agent:s,pinned:w,forced:i,envRespawned:f}}var So,rc,FR,iy,UR,HR,cy,uy=O(()=>{ze();Es();wi();gi();zs();Yt();So=jR(DR),rc=e=>new Promise(t=>setTimeout(t,e)),FR=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,iy=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,UR=250,HR=400,cy={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 dy(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await et(e,t)}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 m=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(m.description||"").trim()||null,detail:String(m.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let m=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(m?go(m):null),detail:m?go(m):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&KR.test(GR(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 KR,GR,fy=O(()=>{vt();yo();KR=/^Async (?:agent|task) launched/i,GR=e=>{if(typeof e=="string")return e.trim();if(!Array.isArray(e))return"";let t=e.find(n=>n&&n.type==="text"&&typeof n.text=="string");return t?t.text.trim():""}});function Hn(e,t){if(!e)return null;let n=String(e);if(t){let o=t.endsWith("/")?t:`${t}/`;if(n===t)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 VR(e){let t=typeof e=="string"?e:String(e?.patch??e?.input??""),n=[];for(let r of t.matchAll(/^\*{3} (?:Update|Add|Delete) File: (.+)$/gm))n.push(r[1].trim());return n}function py(e,t,n){switch(e){case"Shell":{let r=String(t?.command||"").trim();return r?{command:r,description:String(t?.description||"").trim()}:null}case"Read":{let r=t?.path;if(!r)return null;let o=Number(t?.offset)||null,s=o&&Number(t?.limit)?o+Number(t.limit)-1:null;return{command:St(`read ${Hn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${To(r)}`:`Read ${To(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?Hn(t.path,n):null,s=t?.glob?` --glob ${gd(t.glob)}`:"",a=St(String(r),40);return{command:St(`grep ${gd(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=t?.glob_pattern;if(!r)return null;let o=t?.target_directory?Hn(t.target_directory,n):null;return{command:St(`glob ${gd(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${St(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:St(`edit ${Hn(t.path,n)}`,140),description:`Edit ${To(t.path)}`}:null;case"Write":return t?.path?{command:St(`write ${Hn(t.path,n)}`,140),description:`Write ${To(t.path)}`}:null;case"Delete":return t?.path?{command:St(`delete ${Hn(t.path,n)}`,140),description:`Delete ${To(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>Hn(s,n)).filter(Boolean);return{command:St(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=VR(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:St(`apply_patch ${Hn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${To(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:St(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var oc,St,To,gd,my=O(()=>{oc=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),St=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,To=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);gd=e=>`"${String(e).replace(/"/g,'\\"')}"`});import ta from"node:fs";import zR from"node:fs/promises";import Ir from"node:path";import JR from"node:os";import YR from"node:crypto";import XR from"node:zlib";function gy(e){let t=YR.createHash("md5").update(String(e)).digest("hex");return Ir.join(ZR,t)}function yy(e){try{let t=JSON.parse(ta.readFileSync(Ir.join(e,"meta.json"),"utf8"));return{createdAtMs:Number(t.createdAtMs)||null,updatedAtMs:Number(t.updatedAtMs)||Number(t.createdAtMs)||null,hasConversation:t.hasConversation===!0}}catch{return null}}function QR(e){let t=gy(e),n;try{n=ta.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Ir.join(t,o.name),a=Ir.join(s,"store.db");if(!ta.existsSync(a))continue;let i=yy(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 sc(e,t){if(!e)return null;if(t){let n=Ir.join(gy(e),t),r=Ir.join(n,"store.db");if(ta.existsSync(r))return{id:t,dir:n,store:r,...yy(n)||{createdAtMs:null,updatedAtMs:null}}}return QR(e)}function hy(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function e$(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=XR.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function t$(e){let t=[],n=0,r=e.length,o=()=>{let s=0,a=0;for(;n<r;){let i=e[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=e.subarray(n,n+c);n+=c,a===1&&c===32&&t.push(l.toString("hex"))}else if(i===0)o();else if(i===5)n+=4;else if(i===1)n+=8;else break}return t}function n$(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!oc.has(r.toolName))continue;let o=py(r.toolName,r.args,t);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}async function yd(e,t){if(!wy)return Ao;let{DatabaseSync:n}=wy,r;try{r=new n(e,{readOnly:!0})}catch{return Ao}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 Ao;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Ao}let a=s.lastUsedModel||null,i=s.latestRootBlobId?hy(r,s.latestRootBlobId):null;if(!i)return{...Ao,model:a};let c=[],l=[],u=[],f=null,d=[];for(let m of t$(i)){let h=e$(hy(r,m));if(!h)continue;let w=h.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),h.role!=="assistant")continue;if(Array.isArray(h.content))for(let x of h.content){let E=x?.providerOptions?.cursor?.modelName;E&&(f=E)}l.push(...n$(h.content,t).map((x,E)=>({...x,id:x.id||`${m}:${E}`}))),u.push(...o$(h.content));let y=r$(h.content);if(!y)continue;let b=[...new Set(u)];u=[],c.push({id:m,text:y,tools:b.length?b:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return Ao}finally{try{r.close()}catch{}}}function by(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(ta.statSync(e.store).mtimeMs)}catch{n=Date.now()}let r=e.createdAtMs;(!r||r>n)&&(r=n-t*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/t)}}async function ky(e,t,{limit:n=80}={}){let r=sc(e,t);if(!r)return[];let{turns:o,model:s}=await yd(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=by(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 xy(e,t){let n=sc(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await yd(n.store,e);return{chatId:n.id,requestIds:o,models:[...new Set(r.map(a=>a.model).filter(Boolean))],configuredModel:s,updatedAtMs:n.updatedAtMs||null}}async function vy(e,t,{limit:n=80}={}){let r=sc(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await yd(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=by(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:Pn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:Pn(u.command,u.description),ts:i+1});return l}async function Sy(e,t){let n=sc(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await zR.stat(n.store)).mtimeMs)}catch{return null}}var ZR,r$,o$,wy,Ao,bd=O(()=>{yo();my();ZR=process.env.BOSTRAT_CURSOR_CHATS||Ir.join(JR.homedir(),".cursor","chats");r$=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
|
|
113
|
+
`).trim():"",o$=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],wy=process.getBuiltinModule?.("node:sqlite"),Ao={turns:[],trailingCommands:[],model:null,requestIds:[]}});import s$ from"node:fs/promises";function Sd(e){return Ty.set(e.agent,e),e}function na(e){return Ty.get(e)||a$}async function Ad(e,t={}){return(await na(e.agent).readMessages(e,t)).map(Td(e))}async function Ay(e,t={}){return(await na(e.agent).readCommands(e,t)).map(Td(e))}async function Ey(e,t={}){return(await na(e.agent).readJobs(e,t)).map(Td(e))}var Eo,kd,xd,vd,a$,Ty,Td,_y=O(()=>{vt();Ui();yo();fy();bd();Sr();Eo=class{constructor(t,n=new Set){this.agent=t,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},kd=class extends Eo{transcript(t){return!t?.cwd||!t?.sessionId?null:Zt(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?Pu(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?Tw(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?dy(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await s$.stat(n)).mtimeMs)}catch{return null}}},xd=class extends Eo{async readMessages(t,n){return ky(t.cwd,t.sessionId,n)}async readCommands(t,n){return vy(t.cwd,t.sessionId,n)}async updatedAt(t){return Sy(t.cwd,t.sessionId)}},vd=class extends Eo{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Iw(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Rw(t.cwd,this.sessionId(t),n)}async updatedAt(t){return $w(t.cwd,this.sessionId(t))}},a$=new Eo("unknown"),Ty=new Map;Sd(new kd("claude",$u));Sd(new xd("cursor",oc));Sd(new vd("codex",Hs));Td=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function Or(e){let t=e.search(Rd);return(t>=0?e.slice(0,t):e).trim()}function i$(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function c$(e){let t=String(e).trim();return!!(!t||$r.test(e)||Rd.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function l$(e,t=""){let n=[],r=/([☐☒])\s+([^☐☒←→✔]+)/g,o;for(;(o=r.exec(e))!==null;)n.push({label:o[2].trim(),answered:o[1]==="\u2612",current:!1});if(!n.length)return null;let s=u$(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function u$(e,t){let n=String(t).toLowerCase(),r=e.findIndex(c=>!c.answered),o=r>=0?r:e.length-1;if(!n)return o;let s=e.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 Ry(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function d$(e,t=""){let n=String(e);if(!Oy.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(Rr);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&&!$r.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:Or(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:t,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 p$(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
115
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Cy.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(Rr);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&&!(f$.test(n[c])||$r.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Or(c.label),description:""}));return{provider:"claude",box:t,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 m$(e,t=""){let n=String(e);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(Rr);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:t,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 w$(e,t=""){let n=String(e);if(!Ed.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
117
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(h$.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(Rr);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&&!$r.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>Ed.test(u.label)))return null;let i=s.map(u=>({label:Or(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:t,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 $d(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(Rr);o&&t.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=t.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}function g$(e,t=""){let n=String(e);if(!My.test(n)||!Ny.test(n)||Cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
118
|
+
`),o=$d(r);return o?{provider:"claude",box:t,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:Or(s.label),description:""}))}]}:null}function y$(e,t=""){let n=String(e);if(!Py.test(n)||Cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
119
|
+
`),o=$d(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Or(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:t,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 k$(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!b$.test(o)||Od.test(e[r]))break;n+=o}return n}function x$(e,t=""){let n=String(e);if(!Od.test(n)||Cr.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=k$(r,s);break}return o?{provider:"claude",box:t,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 S$(e,t=""){let n=String(e);if(!_d.test(n)||!v$.test(n)||Cr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
121
|
+
`)].reverse().find(a=>_d.test(a))||"OAuth error").trim();return{provider:"claude",box:t,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 T$(e,t=""){let n=String(e);if(!By.test(n)||Cr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
122
|
+
`),o=$d(r);return o?{provider:"claude",box:t,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:Or(s.label),description:""}))}]}:null}function A$(e,t=""){let n=String(e);if(Cr.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(Dy);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($r.test(r[l])||Rd.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:t,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 E$(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(Id.test(e[r])){t=r;break}if(t<0)return null;let n=-1;for(let r=t-1;r>=0;r--)if(/[☐☒]/.test(e[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:t,optStart:n+1,optEnd:t}}function _$(e,t=""){let n=String(e);if(!$y.test(n)||!Id.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
124
|
+
`),o=E$(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 W=r[T].match(Rr);W&&(u<0&&(u=T),l.push({line:T,cursor:!!W[1],checked:W[3]!=null&&W[3]!==" ",hasCheckbox:W[3]!=null,label:W[4].trim(),desc:""}))}if(!l.length)return null;for(let T of l)for(let W=T.line+1;W<Math.min(r.length,T.line+4)&&!(Rr.test(r[W])||$r.test(r[W]));W++){let H=r[W].trim();if(!(!H||c$(r[W])||Iy.test(H))){T.desc=i$(H);break}}let f=l.filter(T=>!Iy.test(T.label));if(!f.length)return null;let d=f.some(T=>T.hasCheckbox),m=Math.max(0,f.findIndex(T=>T.cursor)),h=s>=0?s+1:0,w=[];for(let T=h;T<u;T++){let W=r[T].trim();W&&!$r.test(r[T])&&!/[☐☒]/.test(r[T])&&w.push(W)}let y=w.join(" ")||(s>=0?Ry(r[s]):"")||"The agent is asking a question",b=s>=0?l$(r[s],y):null,x=b?.currentTabLabel||(s>=0?Ry(r[s]):""),E=f.map(T=>({label:Or(T.label),description:T.desc})),_=E.map(T=>T.label).join("|"),C=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${x}:${y}:${_}`.slice(0,240),N=b?`claude:ask:${b.tabs.map(T=>T.label).join("|")}`.slice(0,240):C;return{provider:"claude",box:t,multiSelect:d,cursorIndex:m,promptId:C,groupId:N,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:y,header:x,multiSelect:d,options:E}]}}function Mr(e,t){let n=String(t||"");return n?e==="claude"?$y.test(n)&&Id.test(n)||Oy.test(n)||Ed.test(n)||Cy.test(n.trimEnd().split(`
|
|
125
|
+
`).pop()||"")?!0:Cr.test(n)?!1:My.test(n)&&Ny.test(n)||Py.test(n)||Od.test(n)||_d.test(n)||By.test(n)||Dy.test(n.trimEnd().split(`
|
|
126
|
+
`).pop()||""):e==="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:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function Wn(e,t){return!e||!t?null:e.agent==="claude"?m$(t,e.name)||_$(t,e.name)||d$(t,e.name)||w$(t,e.name)||S$(t,e.name)||x$(t,e.name)||y$(t,e.name)||g$(t,e.name)||T$(t,e.name)||A$(t,e.name)||p$(t,e.name):e.agent==="cursor"||e.agent==="codex"?Kw(e.agent,t,e.name):null}async function Cd(e){if(!e)return null;let t=await Xt(e.name).catch(()=>"");return Wn(e,t)}function jy(e,t){let n=Array.isArray(t?.[0])?t[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(e?.cursorIndex)?e.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(e?.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 Ly(e,t){let n=Number.isInteger(e?.questionIndex)?e.questionIndex:0,o=(Number.isInteger(t)?t:n)-n;if(!o)return[];let s=o>0?"Tab":"BTab";return Array.from({length:Math.abs(o)},()=>s)}var $y,Id,Rr,Iy,$r,Rd,Oy,Cy,f$,Ed,h$,Cr,My,Ny,Py,Od,b$,_d,v$,By,Dy,Md=O(()=>{ze();Hi();$y=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Id=/Chat about this/i,Rr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Iy=/^(type something|chat about this|submit)\b/i,$r=/^[─—-]{6,}\s*$/,Rd=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;Oy=/^\s*Switch model\?\s*$/m;Cy=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,f$=/^\s*[─—▔-]{6,}\s*$/;Ed=/Resume from summary/i,h$=/^\s*This session is\b/;Cr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;My=/Choose the text style that looks best with your terminal/,Ny=/Let's get started\./;Py=/Select login method:/;Od=/Paste code here if prompted/,b$=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;_d=/OAuth error:/,v$=/Press Enter to retry\./;By=/Quick safety check: Is this a project you created or one you trust/;Dy=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function ac(e,t,n){if(!e||!t)return null;for(let r of I$)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function $$(e){let t=Nd.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},Nd.set(e,t)),t}function Hy(e,t,n=Date.now()){let r=$$(e);if(r.sentAt=r.sentAt.filter(s=>n-s<Uy),r.sentAt.length>=Fy)return{action:"give-up"};if(n-r.last<R$)return{action:"wait"};r.last=n,r.sentAt.push(n);let o=!r.emitted.has(t);return o&&r.emitted.add(t),{action:"send",first:o}}function Wy(e,t=Date.now()){let n=Nd.get(e);return n?n.sentAt.filter(r=>t-r<Uy).length>=Fy:!1}var I$,R$,Fy,Uy,Nd,Pd=O(()=>{I$=[{provider:"claude",id:"fullscreen-upsell",match(e,t){let n=e?.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(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];R$=5e3,Fy=5,Uy=3600*1e3,Nd=new Map});function _o(e){return typeof e=="string"?e.trim():""}var qy=O(()=>{});import{execFile as O$}from"node:child_process";import{promisify as C$}from"node:util";async function Ie(e,t,{input:n}={}){let r=Dd("git",["-C",e,...t],{maxBuffer:Ky});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Io(e){if(!e)return!1;try{let{stdout:t}=await Ie(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function Gy(e){let{stdout:t}=await Ie(e,["rev-parse","--show-toplevel"]);return t.trim()}async function M$(e){try{return await Ie(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Ro(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Ie(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Dd("git",["-C",e,...a],{maxBuffer:Ky,env:o,...i});try{let a=await M$(e);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",t]:["commit-tree",c,"-m",t],{stdout:u}=await s(l);return u.trim()}finally{try{await Dd("rm",["-f",r])}catch{}}}async function jd(e,t){let{stdout:n}=await Ie(e,["status","--porcelain"]);return n.trim()?(await Ie(e,["add","-A"]),await Ie(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t]),!0):!1}async function ra(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Ie(e,["status","--porcelain"]),r=n.split(`
|
|
128
|
+
`).filter(Boolean);return t&&(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 oa(e,t){if(!t)return 0;try{let{stdout:n}=await Ie(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function $o(e,t,n,r){return await Ie(e,["worktree","add","--no-track","-b",n,t,r]),t}async function Vy(e,t){try{return await Ie(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function ic(e,t,n){return await Ie(e,["worktree","add",t,n]),t}async function Oo(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Ie(e,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(t.trim())return t.trim()}catch{}for(let[t,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 Ie(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Ld(e){await Ie(e,["init","-q"]),await Ie(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function at(e,t){try{await Ie(e,["worktree","remove","--force",t])}catch{try{await Ie(e,["worktree","prune"])}catch{}}}function N$(e){let t=/(\d+) files? changed/.exec(e),n=/(\d+) insertions?\(\+\)/.exec(e),r=/(\d+) deletions?\(-\)/.exec(e);return{files:t?Number(t[1]):0,insertions:n?Number(n[1]):0,deletions:r?Number(r[1]):0}}function Fd(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Ud(e,t,n,r){let{stdout:o}=await Ie(e,Fd(["diff","--shortstat",t,n],r));return N$(o)}async function Hd(e,t,n,r){let{stdout:o}=await Ie(e,Fd(["diff","--name-only",t,n],r));return o.split(`
|
|
129
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Wd(e,t,n,r){let{stdout:o}=await Ie(e,Fd(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Bd(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Bd(e,o,["--3way","--whitespace=nowarn"]),a=await P$(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Bd(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Bd(e,t,n){try{return await Ie(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function P$(e){try{let{stdout:t}=await Ie(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
130
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Dd,Ky,cc=O(()=>{Dd=C$(O$),Ky=64*1024*1024});import qd from"node:fs/promises";import Jy from"node:path";import B$ from"node:os";function Qy(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function eb(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function tb(e){let t=new Date(Qy(e));return t.setDate(t.getDate()+7),t.getTime()}function nb(e){let t=new Date(eb(e));return t.setMonth(t.getMonth()+1),t.getTime()}function uc(e=Date.now()){for(let[t,n]of Object.entries(Co)){let r=n.startOf(e);if(r!==en[t]){en[t]=r;for(let o of Nr.values())o[t]=0;for(let o of Mo)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Mo.delete(o)}}}function rb(e,t){let n=Tt?.caps?.[e]?.[Co[t].capKey];return Number.isFinite(n)&&n>0?n:null}function Pr(e,t=Date.now()){if(!e||!Gd||!Tt?.caps)return null;uc(t);let n=Nr.get(e);for(let r of Object.keys(Co)){let o=rb(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:lc(s),resetsAt:Co[r].nextStart(t)}}return null}function ob(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${en[t.window]}`;return Mo.has(n)?!1:(Mo.add(n),!0)}function zy(e,t,n,r=Date.now()){if(!e||!t)return;uc(r);let o=D$(e);for(let[s,a]of Object.entries(Co))n>=en[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function j$(e,t,n=Date.now()){if(!Tt?.caps||!Kd)return;let r=Nr.get(e);if(r)for(let[o,s]of Object.entries(Co)){let a=rb(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*Zy||i>=a)continue;let c=`warn:${e}:${o}:${en[o]}`;if(!Mo.has(c)){Mo.add(c);try{let l=await Kd(t);if(!l)continue;await I(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:lc(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function sb(e){let t={};for(let[n,r]of Object.entries(e||{})){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&&(t[n]=o)}return t}async function dc(e,{source:t="local",updatedAt:n=Date.now()}={}){Tt={caps:sb(e),updatedAt:n,source:t};try{await qd.mkdir(Yy,{recursive:!0}),await qd.writeFile(Xy(),JSON.stringify(Tt,null,2))}catch{}return sa()}function sa(e=Date.now()){uc(e);let t=new Set([...Nr.keys(),...Object.keys(Tt?.caps||{})]),n={};for(let r of t){let o=Nr.get(r);n[r]={weeklyUsd:lc(o?.weekly||0),monthlyUsd:lc(o?.monthly||0)}}return{caps:Tt?.caps||{},configured:!!Tt,seeded:Gd,updatedAt:Tt?.updatedAt??null,spent:n,windows:{weekly:{start:en.weekly,resetsAt:tb(e)},monthly:{start:en.monthly,resetsAt:nb(e)}},warnPct:Zy}}async function ab({listTracks:e,getTrack:t}={}){Kd=t||null,uc();try{let o=JSON.parse(await qd.readFile(Xy(),"utf8"));Tt={caps:sb(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{Tt=null}let n=Date.now();tt.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Qt(o);zy(o.provider||"unknown",s,o.ts??Date.now()),j$(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(en.weekly,en.monthly);for(let o of await e?.()||[]){let s;try{s=await B(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||zy(a.provider||"unknown",Qt(a).usd,i,n)}}return Gd=!0,sa()}var Yy,Xy,Zy,Co,Tt,Gd,en,Nr,Mo,Kd,D$,lc,fc=O(()=>{le();_r();Yy=process.env.BOSTRAT_STATE_DIR||Jy.join(process.env.HOME||B$.tmpdir(),".bostrat"),Xy=()=>Jy.join(Yy,"usage-caps.json"),Zy=.8;Co={weekly:{startOf:Qy,nextStart:tb,capKey:"weeklyUsd"},monthly:{startOf:eb,nextStart:nb,capKey:"monthlyUsd"}},Tt=null,Gd=!1,en={weekly:0,monthly:0},Nr=new Map,Mo=new Set,Kd=null,D$=e=>{let t=Nr.get(e);return t||(t={weekly:0,monthly:0},Nr.set(e,t)),t};lc=e=>Math.round(e*1e4)/1e4});import L$ from"node:fs/promises";import F$ from"node:os";import ib from"node:path";function mb(e){return e==="codex"?q$:e==="cursor"?pb:db}function cb(e){let t=String(e||"").split(`
|
|
131
|
+
`),n=t.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[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(t[o]))break}return r.length?r:null}function lb(e){let t=String(e||"").split(`
|
|
132
|
+
`),n=t.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[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(t[o]))break}return r.length?r:null}function K$(e){let t=String(e||"").split(`
|
|
133
|
+
`);for(let n=t.length-1;n>=0;n--){let r=t[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function ub(e){let t=String(e||"").split(`
|
|
134
|
+
`),n=t.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(t[n]),o=[];for(let s=n+1;s<t.length;s++){let a=t[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 hb(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim();if(e==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!fb.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=pb.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!W$.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function aa(e,t,{timeoutMs:n=U$}={}){let r=Date.now()+n;for(;;){let o=await Ae(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await pc(H$)}}async function G$(e,t){return await je(e,`/model ${t.model}`,Mt("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function V$(e,t){await Bi(e),await je(e,"/model",{enter:!1}),await pc(250),await Oe(e,["Enter"],{kind:"model-switch"});let n=await aa(e,cb);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Oe(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=K$(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await je(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await aa(e,i=>lb(i)||!cb(i)&&o(i)&&{done:o(i)});if(!s)throw await Oe(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?fb.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Oe(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await je(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await aa(e,i=>!lb(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 z$(){let e=ib.join(process.env.CURSOR_CONFIG_DIR||ib.join(F$.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await L$.readFile(e,"utf8"))?.model||null}catch{return null}}async function J$(e,t){await Bi(e),await ji(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await aa(e,ub),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(t.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await aa(e,ub,{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 Oe(e,["Down"],{kind:"model-switch"}),await pc(250)}if(!n||!s||!o(s)){await Oe(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Oe(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Oe(e,["Enter"],{kind:"model-switch"}),await pc(800);let a=await z$();return{verified:!!a&&(a.modelId===t.model||a.displayModelId===t.model||r(a.displayName).startsWith(r(t.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function wb(e,t,n){return e==="codex"?V$(t,n):e==="cursor"?J$(t,n):G$(t,n)}var pc,U$,H$,db,W$,q$,fb,pb,gb=O(()=>{ze();zs();pc=e=>new Promise(t=>setTimeout(t,e)),U$=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,H$=300,db=[{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"}],W$=new Set([...db.map(e=>e.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"]),q$=[{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"}],fb=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),pb=[{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 hc(e){let t=e||{},n=Y$(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function Y$(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:nt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:nt(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:nt(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:nt(e.agent),payload:{...qn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:nt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...qn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{change:"subtask-resumed",...qn(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...qn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...qn(e)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...qn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{change:"rehydrated"}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{...qn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...qn(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function kb(e,t){let n=hc(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;zd(()=>I(r,n))}async function xb(e){if(!(!e?.id||yb.has(e.id))){yb.add(e.id);try{let t=await B(e),n=new Set(t.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of e.events||[]){let o=hc(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(e,o)}}catch{}}}function qn(e){let{type:t,at:n,...r}=e;return r}function vb(e,t,n){!n?.promptId||Vd.get(e.id)===n.promptId||(Vd.set(e.id,n.promptId),zd(()=>I(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:nt(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function Jd(e,t,n,r={}){Vd.delete(e.id),zd(()=>I(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:nt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var nt,mc,zd,bb,yb,Vd,Yd=O(()=>{le();nt=e=>e||null;mc=Promise.resolve(),zd=e=>(mc=mc.then(e).catch(()=>{}),mc),bb=()=>mc;yb=new Set;Vd=new Map});function tO(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Ib(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of tO(n).reverse()){if(!X$.test(r))continue;let o=r.endsWith("?")&&r.length<=eO&&Q$.test(r);if(Z$.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function $b(e){let t=String(e||""),n=[];nO.test(t)&&n.push("commit"),Rb.test(t)&&n.push("rebase"),rO.test(t)&&n.push("push");let r=sO.test(t);!r&&oO.test(t)&&n.push("pr");let o=null;if(r){let s=t.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||t.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function Ob(e){return Rb.test(String(e||""))}function Po(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(m=>`${m.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let m=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${m} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${m} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let m=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",h=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${h}${m}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${m}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${h}${m}`:`push this branch${l} to the remote${m}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,m=>m.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}function Cb(e,{branch:t=null,base:n=null,prNumber:r=null,offeredRebase:o=!1}={}){let s=new Set(Array.isArray(e)?e:["commit","push","pr"]),a=t?` on this branch (${t})`:"",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${t?` (${t})`:""}`} 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}`:""}${t?` and its remote branch (${t})`:" and its remote branch"} exactly as they are \u2014 don't push ${t||"the old branch"} itself.`,l}function Mb(e,t=Date.now()){let n=[],r=null;for(let o of e||[]){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&&t-r.lastActivity<=aO&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Nb(e,t){let n=new Set;for(let r of t||[]){if(!r.settled)continue;let o=null;for(let s of e||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to+iO||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Pb(e){let t=(Array.isArray(e)?e:[]).filter(r=>Sb[r]).map(r=>Sb[r]);return`The ship you were given the go-ahead for${t.length?` (${t.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 Tb,Ab,Eb,_b,X$,Z$,Q$,eO,No,nO,Rb,rO,oO,sO,aO,iO,Sb,Bo=O(()=>{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|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,Ab=/(?<!\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,_b=/(?<!\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,X$=new RegExp(`\\b(?:${Tb}|${Ab}|${Eb}|${_b})\\b`,"i"),Z$=/\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,Q$=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,eO=90;No=["commit","rebase","push","pr"],nO=new RegExp(`\\b(?:${Tb})\\b`,"i"),Rb=new RegExp(`\\b(?:${_b})\\b`,"i"),rO=new RegExp(`\\b(?:${Ab})\\b`,"i"),oO=new RegExp(`\\b(?:${Eb})\\b`,"i"),sO=/\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;aO=3e4;iO=2500;Sb={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import cO from"node:crypto";import wc from"node:fs/promises";import Bb from"node:path";import lO from"node:os";async function Ce(){return Kn||(Xd??=wc.readFile(Qd,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(Kn=e,!t&&Te()){let{records:n,source:r}=await Be("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;Kn[X(i.repo,i.number)]=i}Object.keys(Kn).length&&await ef().catch(()=>{})}}return Kn}).finally(()=>Xd=null),Xd)}async function ef(){let e=JSON.stringify({links:Kn},null,2),t=`${Qd}.${process.pid}.tmp`;await wc.mkdir(Db,{recursive:!0}),await wc.writeFile(t,e),await wc.rename(t,Qd)}async function Fe(e){Zd=Zd.then(ef,ef),await Zd.catch(()=>{});let t=Kn?.[e];t&&Ve("prlinks",{id:jb(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function it({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await Ce(),c=X(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:uO.has(a),firstSeen:Date.now()},await Fe(X(e,t)),{link:i[c],created:!0})}async function tn(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||n.announced?null:(n.announced=!0,await Fe(X(e,t)),n)}async function Je(e,t){return!e||t==null?null:(await Ce())[X(e,t)]??null}async function Gn(e,t,n){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];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 Fe(X(e,t)),r):null}async function Lb(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[X(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Fe(X(e,t)),o):null}async function Fb(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[X(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Fe(X(e,t)),o):null}async function gc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Fe(X(e,t)),r)}async function yc(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Fe(X(e,t)),n)}async function bc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Fe(X(e,t)),r)}async function kc(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Fe(X(e,t)),n)}async function Ub(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return r?(r.baseFoldAt=n,await Fe(X(e,t)),r):null}async function Hb(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Fe(X(e,t)),n)}async function Wb(e,t,n){if(!e||t==null||!n)return null;let r=(await Ce())[X(e,t)];return r?(r.lastHeadSha=n,await Fe(X(e,t)),r):null}async function ia(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Fe(X(e,t)),r):null}async function Br(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Fe(X(e,t)),r):null}async function ca(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Fe(X(e,t)),r):null}async function qb(e,t,n){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];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 Fe(X(e,t)),r}async function la(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Fe(X(e,t)),n)}async function Kb(){return Object.values(await Ce()).filter(e=>!dO.has(e.state))}async function Dr(){return Object.values(await Ce()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function Gb(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ce(),r=0;for(let[o,s]of Object.entries(n))await Ve("prlinks",{id:jb(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Db,Qd,X,jb,Kn,Xd,Zd,uO,dO,Vn=O(()=>{On();Db=process.env.BOSTRAT_STATE_DIR||Bb.join(process.env.HOME||lO.tmpdir(),".bostrat"),Qd=Bb.join(Db,"pr-links.json"),X=(e,t)=>`${e}#${Number(t)}`,jb=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=cO.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},Kn=null,Xd=null,Zd=Promise.resolve();uO=new Set(["api","observed","reconciled"]);dO=new Set(["merged","closed","lost"])});import fO from"node:fs";import tf from"node:fs/promises";import Yb from"node:path";import pO from"node:os";function Qb(e,t=!1){if(!t&&e-zb<mO)return;zb=e;let n=JSON.stringify({windowStart:Do,byCaller:Object.fromEntries(nn),lastHour:zn});Jb=Jb.then(()=>tf.mkdir(Xb,{recursive:!0})).then(()=>tf.writeFile(`${xc}.tmp`,n)).then(()=>tf.rename(`${xc}.tmp`,xc)).catch(()=>{})}function ek(e){e-Do<Zb||(zn={start:Do,total:vc(nn),byCaller:Object.fromEntries(nn)},zn.total&&console.log(`[github] ${zn.total} calls last hour`,zn.byCaller),Do=e,nn=new Map,Qb(e,!0))}function rn(e="other",t=Date.now()){ek(t),nn.set(e,(nn.get(e)||0)+1),Qb(t)}function tk(e=Date.now()){return ek(e),{windowStart:Do,total:vc(nn),byCaller:Object.fromEntries(nn),lastHour:zn,backoffUntil:jr>e?jr:null}}function Jn(e){return hO.test(String(e?.message||e||""))}function Yn(e,t=Date.now()){return jr<=t&&console.warn(`[github] rate limited \u2014 backing off ${Vb/6e4} min:`,String(e?.message||e||"").slice(0,200)),jr=Math.max(jr,t+Vb),jr}function Sc(e=Date.now()){return Math.max(0,jr-e)}var Xb,xc,Zb,Vb,mO,Do,nn,zn,jr,zb,vc,Jb,hO,ua=O(()=>{Xb=process.env.BOSTRAT_STATE_DIR||Yb.join(process.env.HOME||pO.tmpdir(),".bostrat"),xc=Yb.join(Xb,"github-metrics.json"),Zb=60*6e4,Vb=5*6e4,mO=6e4,Do=Date.now(),nn=new Map,zn=null,jr=0,zb=0,vc=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(fO.readFileSync(xc,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Zb?(Do=e.windowStart,nn=new Map(Object.entries(e.byCaller||{})),zn=e.lastHour||null):vc(new Map(Object.entries(e.byCaller||{})))&&(zn={start:e.windowStart,total:vc(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Jb=Promise.resolve();hO=/rate limit|too quickly|abuse detection|HTTP 429/i});import nk from"node:fs/promises";import rk from"node:path";import wO from"node:os";function Ac({change:e,repo:t=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:e,repo:t,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};Tc.push(l),Tc.length>yO&&Tc.shift();let u=nf.get(e)||{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)),nf.set(e,u),bO(l),l}async function bO(e){try{await nk.mkdir(ok,{recursive:!0}),await nk.appendFile(gO,JSON.stringify(e)+`
|
|
135
|
+
`)}catch{}}function sk(){let e={};for(let[t,n]of nf)e[t]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:e,recent:Tc.slice(-20)}}var ok,gO,yO,Tc,nf,Ec=O(()=>{ok=process.env.BOSTRAT_STATE_DIR||rk.join(process.env.HOME||wO.tmpdir(),".bostrat"),gO=rk.join(ok,"github-latency.ndjson"),yO=100,Tc=[],nf=new Map});import{execFile as kO}from"node:child_process";import{promisify as xO}from"node:util";async function vO(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Lo("git",["-C",e,"rev-parse","HEAD"]),Lo("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await I({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function mt(e,t,n={}){let{caller:r="prs",...o}=n,s=await $t({cwd:e});rn(r);try{let{stdout:a}=await Lo("gh",t,{cwd:e,...SO,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Jn(a)&&Yn(a),a}}async function sf(){if(_c&&Date.now()-_c.at<6e4)return _c.data;let e;try{rn("auth");let t=await Lo("gh",["auth","status"],{timeout:1e4});e={installed:!0,authenticated:!0,login:`${t.stdout||""}${t.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(t){e=t?.code==="ENOENT"||t?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(e.installed&&!e.authenticated)try{let t=await Rs();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return _c={at:Date.now(),data:e},e}async function TO(e){try{let{stdout:t}=await Lo("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function ik(e){let t=e.name||e.context||"check",n=e.detailsUrl||e.targetUrl||null,r=e.completedAt||e.createdAt||null,o;return e.__typename==="StatusContext"?o=e.state==="SUCCESS"?"pass":e.state==="PENDING"||e.state==="EXPECTED"?"pending":"fail":e.status&&e.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(e.conclusion)?o="pass":o="fail",{name:t,state:o,url:n,completedAt:r}}function rf(e){let t=(e||[]).map(ik),n=t.filter(s=>s.state==="pending").length,r=t.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=t.length&&!n&&r.length?Math.max(...r):null;return{total:t.length,passed:t.filter(s=>s.state==="pass").length,failed:t.filter(s=>s.state==="fail").length,pending:n,failing:t.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}function cf(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function lk(e,t){if(!t)return null;let n=await de(e);if(!n)return null;try{let r=await mt(e,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(t)}&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 uk(e){return(e||[]).map(t=>({name:t.name,detailsUrl:t.html_url||null,status:String(t.status||"").toUpperCase(),conclusion:t.conclusion?String(t.conclusion).toUpperCase():null,completedAt:t.status==="completed"&&t.updated_at||null}))}async function dk(e,t){let n=await lk(e,t);return n===null?{...rf([]),unavailable:!0}:rf(uk(n))}function Ic(e,t=rf(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function EO(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){jo.delete(s);continue}r.add(s),jo.has(s)||jo.set(s,n),o.mergeabilityPending=n-jo.get(s)<AO}for(let o of jo.keys())o.startsWith(`${e}#`)&&!r.has(o)&&jo.delete(o);return t}async function Fo(e,t){let n=await on(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await mt(n.path,["pr","view",String(r),"--json",af]);return Ic(JSON.parse(o))}catch(o){if(!cf(o))throw o;let s=await mt(n.path,["pr","view",String(r),"--json",ck]),a=JSON.parse(s);return Ic(a,await dk(n.path,a.headRefOid))}}async function Nt(e,{caller:t="prs"}={}){try{let n=await mt(e,["pr","list","--json",af,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>Ic(r)).sort(ak)}catch(n){if(!cf(n))throw n;let r=await mt(e,["pr","list","--json",ck,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Ic(o,await dk(e,o.headRefOid))))).sort(ak)}}function Bt(){da.clear()}async function RO(e,t){if(!e)return t;for(let n of t){let r=await Je(e,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 gc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await bc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function fk(){let e=await sf();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await zt())await TO(a.path)&&t.push(a);let n=Date.now(),r=Sc(n)>0,o=await Promise.all(t.map(async a=>{let i=da.get(a.path),c=i?.pending?IO:_O;if(i&&(r||n-i.at<c))return i.entry;try{let l=await de(a.path).catch(()=>null),u=EO(a.path,await Nt(a.path,{caller:"tab"}),n);if(await RO(l,u),i?.entry?.prs)for(let m of u)i.entry.prs.find(w=>w.number===m.number)?.checks?.pending>0&&m.checks.total>0&&m.checks.pending===0&&Ac({change:m.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:m.number,githubAt:m.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(m=>m.mergeabilityPending||m.checks.pending>0||m.autoMergeArmed);return da.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:Pt(l)}}})),s=new Set(t.map(a=>a.path));for(let a of da.keys())s.has(a)||da.delete(a);return{auth:e,repos:o}}async function on(e){let t=(await zt()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Lr(e){return ro(e)}async function Uo(e,t,{method:n="squash"}={}){let r=await on(e);if(!fa.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(t);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await $t({cwd:r.path})).GH_TOKEN){let a=await tu().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await mt(r.path,s)}catch(a){throw new Error(Pt(a))}finally{Bt()}return{ok:!0,number:o}}async function pa(e,t){let n=await on(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await mt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Pt(s))}finally{Bt()}let o=await de(n.path).catch(()=>null);return o&&await Ub(o,r).catch(()=>{}),{ok:!0,number:r}}async function lf(e,t){let n=await on(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await mt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Pt(o))}finally{Bt()}return{ok:!0,number:r}}function uf(e){return e.green&&!e.isDraft}function MO(e){return e.isDraft?"draft":e.mergeable==="CONFLICTING"?`conflicts with ${e.baseRefName}`:e.checks.failed>0?"checks failing":e.checks.pending>0?"checks running":e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":e.mergeStateStatus==="BEHIND"?`behind ${e.baseRefName}`:e.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${e.mergeStateStatus})`}function pk(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:uf(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function mk(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function hk(){let e={};for(let[t,n]of of)e[t]={...n};return e}async function wk(e,{method:t="merge",onMerged:n=null}={}){let r=await on(e);if(!fa.has(t))throw new Error(`invalid merge method: ${t}`);if(of.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Nt(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:t,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),PO(s,o,{listPrs:()=>Nt(r.path),merge:a=>Uo(r.path,a,{method:t}),sleep:CO,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function NO(e,t){let n=Date.now()+(t.settleMaxMs??OO),r=null;for(;;){let o=null;try{if(o=await t.listPrs(),r=null,!o.some(a=>e.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 t.sleep($O)}}async function PO(e,t,n){try{let r=t;for(let o=0;o<t.length;o++){let s=t[o];if(e.current=s.number,o>0)try{r=await NO(t.slice(o).map(i=>i.number),n)}catch(i){for(let c of t.slice(o))e.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${Pt(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){e.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!uf(a)){e.skipped.push({number:a.number,title:a.title,reason:MO(a)});continue}try{await n.merge(a.number),e.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){e.failed.push({number:a.number,title:a.title,reason:Pt(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Bt()}}async function df(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Lo("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Pt(l)}`)}await vO(e);let o=n||"";if((await $t({cwd:e})).GH_TOKEN){let l=await tu().catch(()=>null);l&&(o=`${o?`${o}
|
|
136
|
+
|
|
137
|
+
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await mt(e,s)}catch(l){throw new Error(Pt(l))}finally{Bt()}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 gk(e,t){let n=await on(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await mt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Pt(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!cf(i))throw i;let c=await mt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await lk(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Pt(i));o=uk(l).map(ik).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=BO(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await mt(n.path,["run","view",c,"--log-failed"]).then(u=>DO(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function BO(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function DO(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
138
|
+
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
139
|
+
`+o.slice(-t).join(`
|
|
145
140
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
146
|
-
`+r.slice(-n)),r}function
|
|
147
|
-
`).find(n=>n.trim())||"gh failed"}var Do,IC,Cc,Mf,Yk,$C,Bo,zk,CC,MC,da,fa,PC,BC,DC,Of,ma=C(()=>{cr();qt();Wn();Zs();Oc();st();Do=EC(AC),IC={timeout:3e4,maxBuffer:8*1024*1024};Cc=null;Mf="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Yk=Mf.replace("statusCheckRollup,","");$C=9e4,Bo=new Map;zk=(t,e)=>t.number-e.number;CC=3e4,MC=1500,da=new Map;fa=new Set(["squash","merge","rebase"]);PC=2500,BC=9e4,DC=t=>new Promise(e=>setTimeout(e,t));Of=new Map});function Nc(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>qC?null:n}function KC(t,e){return t?.firstSeen==null||e?.ts==null||t.firstSeen<e.ts}async function ax(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await B(t)}catch{return}let r=Nc(n);if(r==null)return;let o=(t.boxes||[]).find(u=>u.name===t.leadBox);if(await Mr(t.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(jf.get(t.id)||0)<WC)return;jf.set(t.id,s);let a;try{a=await Pr(t.cwd,{caller:"reconcile"})}catch{return}let i=await Co(t).catch(()=>[e]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await ut(t.cwd).catch(()=>null);if(l){let{link:u,created:d}=await se({repo:l,number:c.number,trackId:t.id,url:c.url,headRef:c.headRefName||e,title:c.title,source:"reconciled"});if(!d&&!(u?.trackId===t.id&&await Qe(l,c.number))){if(!KC(u,r))return;let f=await B(t).catch(()=>null);if(f&&Nc(f)==null)return;await I(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:e,repo:l}});return}}await I(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:e,...l?{repo:l}:{}}}).catch(()=>{}),jf.delete(t.id)}var WC,jf,qC,Lf=C(()=>{st();ma();cr();Wn();ra();Mo();WC=6e3,jf=new Map,qC=30*6e4});import Ff from"node:os";import{execFile as GC}from"node:child_process";import{promisify as VC}from"node:util";async function Pc(){if(process.platform!=="darwin")return Ff.freemem();try{let{stdout:t}=await zC("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:Ff.freemem()}catch{return Ff.freemem()}}var zC,Uf=C(()=>{zC=VC(GC)});import Rt from"node:fs/promises";import Ot from"node:path";import lx from"node:os";import{randomUUID as JC}from"node:crypto";import{execFile as YC}from"node:child_process";import{promisify as XC}from"node:util";import{chromium as ZC}from"playwright-core";async function Le(){return Dr||(Hf||=(async()=>{try{Dr=JSON.parse(await Rt.readFile(dx,"utf8"))}catch{Dr={mocks:[]}}finally{Hf=null}return Dr.mocks||=[],Dr})(),Hf)}async function pe(){await Xt(dx,Dr)}function mx(t,e){je(t,e.v,e.videoRel?.endsWith(".webm")?"webm":"mp4"),e.posterRel&&je(t,e.v,"png")}function zt(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 me(t,e){let n=wa(t.cwd);return{html:Ot.join(n,`${t.id}.v${e}.html`),png:Ot.join(n,`${t.id}.v${e}.png`)}}function ha(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?Ot.join(t.cwd,r):null}function Te(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 Kf(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function Bc(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(iM)}function cM(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 lM(t){let e=Buffer.alloc(24),n=await Rt.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function uM({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 dM(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((h,w)=>{r.onload=h,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,p=-1;for(let h=0;h<s;h++)for(let w=h*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),h<d&&(d=h),p=h);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:p}}},e.toString("base64"))}async function fM(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 pM({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await ZC.launch({executablePath:tM,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Se("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Fo})).newPage();a.setDefaultTimeout(Uo),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Uo}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Uo});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await dM(a,i),u=l&&uM({...l,pad:rM*Fo,align:Fo});u&&(i=await fM(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<fx)throw new Se(`capture produced a ${i.length}-byte image`);let c=Kf(i);if(!c)throw new Se("output is not a PNG");return await Rt.writeFile(e,i),{width:Math.round(c.width/Fo),height:Math.round(c.height/Fo),dpr:Fo,renderMs:Date.now()-r}}catch(s){throw s instanceof Se?s:new Se(/timeout/i.test(s?.message||"")?`render timed out after ${Uo}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function cx({htmlPath:t,outPath:e,width:n}){let r=await Rt.mkdtemp(Ot.join(lx.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await qf(eM,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Uo,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Se(c?.killed?`render timed out after ${Uo}ms`:"firefox exited non-zero",s)}let a=await Rt.stat(e).catch(()=>null);if(!a||a.size<fx)throw new Se(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Kf(await lM(e));if(!i)throw new Se("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Rt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function mM(t){if(QC==="firefox")return cx(t);try{return await pM(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return cx(t);throw e}}function hx(t){return Wf=Wf.then(t,t),Wf}async function wx(t,e){let n=await Le(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await pe(),await zt(r,"rendering",{version:e});try{let o=await Pc();if(o<oM)throw new Se(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=me(r,e),a=await hM({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await Rt.rm(s.png,{force:!0}).catch(()=>{});return}Gf(r,{v:e,source:"html",htmlRel:Ot.relative(r.cwd,s.html),pngRel:Ot.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await pe(),await zt(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),De(r),je(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await pe(),await zt(r,"error",{version:e,error:r.error}),De(r)}}}function Gf(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 wM({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?nM:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function ga({trackId:t,group:e}={}){let n=await Le(),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 an(t){let n=(await Le()).mocks.find(r=>r.id===t);return n?{...n}:null}function gx({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&&!Bc(e))throw new Error("image must be a PNG");if(n){let o=cM(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>ix)throw new Error(`video must be ${ix/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function yx({trackId:t,name:e,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=wM({trackId:t,name:e,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=gx({html:n,image:r,video:o}),h=await Le(),w={id:JC().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 Rt.mkdir(wa(s),{recursive:!0}),h.mocks.push(w),f?(await Rt.writeFile(me(w,1).html,n),await pe(),await zt(w,"created"),De(w),je(w,1,"html"),hx(()=>wx(w.id,1))):o?(await kx(w,1,o,p),await pe(),await zt(w,"created"),await zt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),De(w),mx(w,w.versions[0])):(await xx(w,1,r),await pe(),await zt(w,"created"),await zt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),De(w),je(w,1,"png")),{...w}}async function bx(t,{html:e,image:n,video:r,width:o}={}){let a=(await Le()).mocks.find(u=>u.id===t);if(!a)throw new Error("mock not found");let i=typeof e=="string"&&e.trim(),c=gx({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 Rt.mkdir(wa(a.cwd),{recursive:!0}),i?(a.state="queued",await Rt.writeFile(me(a,l).html,e),await pe(),await zt(a,"updated",{version:l}),De(a),je(a,l,"html"),hx(()=>wx(a.id,l))):r?(await kx(a,l,r,c),await pe(),await zt(a,"updated",{version:l}),await zt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:Te(a)?.durationS??null}),De(a),mx(a,a.versions.find(u=>u.v===l))):(await xx(a,l,n),await pe(),await zt(a,"updated",{version:l}),await zt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),De(a),je(a,l,"png")),{...a}}async function gM(t){try{let{stdout:e}=await qf(sM,["-v","error","-print_format","json","-show_format","-show_streams",t],{timeout:px,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 yM(t,e){try{await qf(aM,["-y","-v","error","-i",t,"-frames:v","1","-f","image2",e],{timeout:px,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Rt.stat(e).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function kx(t,e,n,r){let o=wa(t.cwd),s=Ot.join(o,`${t.id}.v${e}.${r.ext}`),a=Ot.join(o,`${t.id}.v${e}.poster.png`);await Rt.writeFile(s,n);let i=await gM(s),c=await yM(s,a);Gf(t,{v:e,source:"video",htmlRel:null,pngRel:null,videoRel:Ot.relative(t.cwd,s),posterRel:c?Ot.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 xx(t,e,n){let r=Kf(n);if(!r)throw new Error("image must be a PNG");let o=me(t,e);await Rt.writeFile(o.png,n),Gf(t,{v:e,source:"image",htmlRel:null,pngRel:Ot.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function vx(t){let e=await Le(),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 zt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Sx(t){let e=await Le(),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=me(r,o);await Rt.rm(s.html,{force:!0}).catch(()=>{}),await Rt.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Rt.rm(Ot.join(r.cwd,s),{force:!0}).catch(()=>{});return await pe(),await zt(r,"deleted"),ye("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Tx(t){let n=(await Le()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Ot.join(ux,"mock-archive",t.id),o=wa(t.cwd),s=Ot.join(r,".bostrat","mocks");await Rt.mkdir(s,{recursive:!0});let a=await Rt.readdir(o).catch(()=>[]);for(let i of a)await Rt.copyFile(Ot.join(o,i),Ot.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 Rt.stat(Ot.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(),De(i)}return await pe(),n.length}async function Ax(t){let e=await Le(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await pe();for(let r of n)ye("mocks",r.id).catch(()=>{})}return n.length}async function Ex(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Mt("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Le();for(let r of n.mocks){await De(r);for(let o of r.versions||[])o.htmlRel&&await je(r,o.v,"html"),(o.pngRel||o.posterRel)&&await je(r,o.v,"png"),o.videoRel&&await je(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var qf,ux,dx,QC,tM,eM,nM,Fo,rM,fx,Uo,oM,ix,sM,aM,px,Se,Dr,Hf,De,je,wa,iM,hM,Wf,Ho=C(()=>{st();Sn();Uf();An();qf=XC(YC),ux=process.env.BOSTRAT_STATE_DIR||Ot.join(process.env.HOME||lx.tmpdir(),".bostrat"),dx=Ot.join(ux,"mocks.json"),QC=process.env.BOSTRAT_MOCK_ENGINE||"chromium",tM=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",eM=process.env.BOSTRAT_FIREFOX_BIN||"firefox",nM=1300,Fo=2,rM=32,fx=1500,Uo=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,oM=700*1024*1024,ix=100*1024*1024,sM=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",aM=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",px=2e4,Se=class extends Error{constructor(e,n=""){super(n?`${e}
|
|
141
|
+
`+r.slice(-n)),r}function Pt(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
142
|
+
`).find(n=>n.trim())||"gh failed"}var Lo,SO,_c,af,ck,AO,jo,ak,_O,IO,da,fa,$O,OO,CO,of,Ho=O(()=>{hr();Ge();Vn();ua();Ec();le();Lo=xO(kO),SO={timeout:3e4,maxBuffer:8*1024*1024};_c=null;af="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",ck=af.replace("statusCheckRollup,","");AO=9e4,jo=new Map;ak=(e,t)=>e.number-t.number;_O=3e4,IO=1500,da=new Map;fa=new Set(["squash","merge","rebase"]);$O=2500,OO=9e4,CO=e=>new Promise(t=>setTimeout(t,e));of=new Map});import{execFile as jO}from"node:child_process";import{promisify as LO}from"node:util";async function UO(e,t){try{let{stdout:n}=await FO("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function HO(e,t,n,{rebase:r=!1,listPrs:o=Nt}={}){let s=await ra(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await oa(e.dir,n).catch(()=>0),i=t?await UO(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(w=>w.headRefName===t)||null,u=l?"open":"none"}catch{}let f=s||a>0,d=[];s&&d.push("commit"),r&&d.push("rebase"),(s||c)&&d.push("push"),f&&(u!=="open"||s||c)&&d.push("pr");let m=d.filter(h=>!(h==="pr"&&u==="open"));return{name:e.name??null,slug:e.repo??null,dirty:s,unpushed:c,aheadBase:a,prState:u,openPr:l?{number:l.number,url:l.url??null,title:l.title??null}:null,available:Rc.filter(h=>d.includes(h)),defaults:Rc.filter(h=>m.includes(h))}}async function yk(e,{branch:t,rebase:n=!1,listPrs:r=Nt}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(d=>d?.dir):o?.base&&(o.dir||e?.cwd)?[{name:o.base?.split("/").pop()??null,dir:o.dir||e.cwd,repo:o.repo??null,baseRef:o.baseRef}]:[];if(!s.length||!await Io(s[0].dir).catch(()=>!1))return null;let a=[];for(let d of s)a.push(await HO(d,t,d.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(d=>d.dirty||d.aheadBase>0||d.openPr),c=i.length?i:a,l=Rc.filter(d=>c.some(m=>m.available.includes(d))),u=Rc.filter(d=>c.some(m=>m.defaults.includes(d))),f=c.filter(d=>d.openPr);return{available:l,defaults:u,prState:f.length?"open":c.every(d=>d.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(d=>({name:d.name,slug:d.slug,dirty:d.dirty,unpushed:d.unpushed,prState:d.prState,openPr:d.openPr}))}}var FO,Rc,bk=O(()=>{cc();Ho();FO=LO(jO),Rc=["commit","rebase","push","pr"]});function $c(e,t,n){let{state:r,reason:o}=Uu(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of ma)a-c.at>=kk()&&ma.delete(i);ma.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=ma.get(e);return s?Date.now()-s.at>=kk()?(ma.delete(e),!1):s.busy:!1}async function ff(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Ae(e,{lines:De}).catch(()=>"");if(!$c(e,t,r))return Jw(t,r)?{working:!0,prompted:!1}:n;if(Mr(t,r)){let o=await Xt(e).catch(()=>r),s=Wn({name:e,agent:t},o);if(s)return!ac({name:e,agent:t},s,o)||Wy(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function Fr(e,t){return(await ff(e,t)).working}var kk,ma,ha=O(()=>{ze();Gi();Md();Pd();kk=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,ma=new Map});import{execFile as WO}from"node:child_process";import{promisify as qO}from"node:util";async function Wo(e){if(!e)return null;let t=xk.get(e);if(t&&Date.now()-t.at<GO)return t.branch;let n=null;try{let{stdout:r}=await KO("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return xk.set(e,{at:Date.now(),branch:n}),n}function vk(e){return String(e||"").match(VO)?.[1]??null}function zO(e,t){let n=vk(e);return!!n&&!!t&&n===t}function Sk(e,t){let n=vk(e);return!!n&&n!==t}function JO(e){let t=e?.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.dir).filter(Boolean):[t?.dir||e?.cwd].filter(Boolean)}async function qo(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of JO(e))n(await Wo(r));return t}async function Oc(e,t,n=()=>!0){if(!t)return null;let r=(e??[]).filter(a=>a&&n(a)),o=r.find(a=>a.workspace?.branch===t);if(o)return o;let s=r.find(a=>zO(t,a.id));if(s)return s;for(let a of r)if(!(a.archivedAt||a.transferredTo)&&(await qo(a)).includes(t))return a;return null}var KO,GO,xk,VO,Ko=O(()=>{KO=qO(WO),GO=5e3,xk=new Map;VO=/^track\/.+-([0-9a-f]{8})$/});import{execFile as YO}from"node:child_process";import{promisify as XO}from"node:util";async function pf(e,t,{midTurn:n=Fr,pollMs:r=ZO,capMs:o=QO}={}){let s=Date.now()+o;for(;await n(e,t).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}function Rk(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){Tk.lastIndex=n;let o=Tk.exec(t);if(!o)break;n=o.index+o[0].length;let s=o[1]==="-",a=o[2]??o[3]??o[4],i=t.indexOf(`
|
|
143
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+aC(a)+"[ \\t]*$","m").exec(t.slice(i+1));if(!l)continue;let u=i+1+l.index+l[0].length;t=t.slice(0,i)+t.slice(u)}return t}function mf(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function Ok(e){let t=Rk(e),n=t.match(rC);if(!n)return null;let r=mf(t,n.index);return sC.test(r)?null:{repo:r.match($k)?.[1]??null,head:r.match(iC)?.[1]??null}}function Ck(e){let t=Rk(e),n=t.match(oC);if(!n)return null;let r=mf(t,n.index),o=mf(t,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("-")){lC.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match($k)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function dC(e,t,{exclude:n=()=>!1}={}){let r=async i=>{rn("observer");let{stdout:c}=await Ak("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",uC],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Jn(l)&&Yn(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-eC&&!n(c)),s=await $t({slug:e}),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 fC(e,t){if(t)return[t];let n=[];for(let r of Mk(e)){let o=await de(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await zt().catch(()=>[])){let o=await de(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Mk(e){let t=e.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.base).filter(Boolean):[t?.base||e.cwd].filter(Boolean)}async function pC(e,t,n){let r=await qo(e).catch(()=>[e.workspace?.branch].filter(Boolean)),o=t?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||cC(c.headRefName,e.id),i=[];for(let c of await fC(e,t?.repo??null)){let l;try{l=await dC(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await Je(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===e.id&&f.announced)return{settled:!0};if(f&&f.trackId!==e.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==e.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 Nk(e,t,{delays:n=Ek,turnEnd:r}={}){let o=Ok(t.command);if(!o)return null;let s=!!Ck(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Xn.has(a))return null;Xn.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await pC(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:m,created:h}=await it({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!h&&!(m?.trackId===e.id&&await tn(f,d.number))?null:(await pf(t.box,t.agent,r),await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.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{Xn.delete(a)}})();return c.catch(()=>Xn.delete(a)),c}function Pk(e,t,{delays:n=Ek,turnEnd:r,openedWait:o}={}){let s=Ck(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Xn.has(a))return null;Xn.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let f=[];if(c)f=[c];else{let h=Mk(e);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let y=await de(w).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=(await qo(e).catch(()=>[])).filter(h=>h!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(h,w)=>{let y=await Je(c,h.number);return y&&y.trackId!==e.id?null:(y||await it({repo:c,number:h.number,trackId:e.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await ia(c,h.number),w&&((await B(e).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===h.number)||(await pf(t.box,t.agent,r),await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},m=null;for(let h of n){await new Promise(C=>setTimeout(C,h));let w=[];for(let C of f)try{rn("observer");let{stdout:N}=await Ak("gh",["pr","view",String(l),"--repo",C,"--json",mC],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await $t({slug:C})}}),T=JSON.parse(N||"null");T?.number&&w.push({slug:C,pr:T})}catch(N){Jn(N)&&Yn(N)}let y=w.find(C=>C.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let b=y.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(m=b,s.auto)return d(b,!0);continue}let x=await Je(c,b.number);if(x&&x.trackId!==e.id)return null;x||await it({repo:c,number:b.number,trackId:e.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await pf(t.box,t.agent,r);let E=await B(e).catch(()=>[]);if(E.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="merged"&&C.payload?.number===b.number))return null;let _=C=>C.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="opened"&&N.payload?.number===b.number),q=0;if(!_(E)){if(Ok(t.command)&&await tn(c,b.number)){let C=Date.now();q=C+1,await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:C,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await Je(c,b.number))?.announced){let C=o?.capMs??tC,N=o?.pollMs??nC,T=Date.now()+C;for(;Date.now()<T&&(await new Promise(W=>setTimeout(W,N)),!_(await B(e).catch(()=>[]))););}}return await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),q),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await an(c,b.number).catch(()=>{}),await sn(c,b.number).catch(()=>{}),await Gn(c,b.number,"merged"),b}return m?d(m,!1):null}finally{Xn.delete(a)}})();return i.catch(()=>Xn.delete(a)),i}var Ak,Ek,ZO,QO,eC,tC,nC,_k,Ik,rC,oC,sC,Tk,aC,$k,iC,cC,lC,uC,Xn,mC,Bk=O(()=>{le();hr();Ge();ua();Vn();Ur();ha();Ko();Ak=XO(YO),Ek=[4e3,15e3,45e3],ZO=2500,QO=5*6e4;eC=15e3,tC=9e4,nC=2e3,_k=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,Ik=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,rC=new RegExp(`${_k}${Ik}create\\b`),oC=new RegExp(`${_k}${Ik}merge\\b`),sC=/\s--web\b/,Tk=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,aC=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");$k=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,iC=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,cC=Sk;lC=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);uC="number,url,title,headRefName,createdAt";Xn=new Set;mC="number,url,title,headRefName,state"});import hC from"node:fs/promises";import wf from"node:path";import wC from"node:os";function hf(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Cc(e){let t=gC.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=hf(n,"from"),s=o?o.replace(/^uds:/,""):null,a=Dk(s);return{socket:s,pid:a,name:hf(n,"from-name"),mode:hf(n,"from-mode"),text:r.trim()}}function Dk(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function yC(){return process.env.CLAUDE_CONFIG_DIR?wf.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):wf.join(wC.homedir(),".claude","sessions")}async function bC(e){let t=Dk(e);if(!t)return null;try{let n=await hC.readFile(wf.join(yC(),`${t}.json`),"utf8"),r=JSON.parse(n);return{pid:t,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function jk(e,t){let n=await bC(e?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof t=="function")try{o=await t(n?.sessionId??null,r)??null}catch{o=null}return{socket:e?.socket??null,pid:n?.pid??e?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??e?.name??null,mode:e?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}var gC,gf=O(()=>{gC=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function yf(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function bf({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!yf(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),Lk);try{let a=await Fk(`${be(r)}/v1/loops/${encodeURIComponent(e)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:t,...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 Uk(e,{provider:t=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!yf(s))return{ok:!1,source:"unconfigured"};let i=Mc.get(e.id)||0;if(a()-i<kC)return{ok:!1,source:"debounced"};Mc.set(e.id,a());let c=await Nc({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return Mc.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),f=await bf({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||Mc.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function Nc({env:e=process.env}={}){if(!yf(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),Lk);try{let r=await Fk(`${be(e)}/v1/loops`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},signal:t.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)}}var Lk,Fk,kC,Mc,kf=O(()=>{Ge();Lk=1e4,Fk=(...e)=>fetch(...e);kC=10*6e4,Mc=new Map});import{randomUUID as xC}from"node:crypto";function wa(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function Hk(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function Wk(e){return`job|${e.box}|id:${e.id}`}async function vf(e){return xf.has(e.id)?xf.get(e.id):(Pc.has(e.id)||Pc.set(e.id,B(e).then(t=>{let n={keys:new Set,cmdN:0};for(let r of t)if((r.type==="turn.user"||r.type==="turn.agent"||r.type==="provider.error")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(wa({role:o,sourceId:r.payload.sourceId})),n.keys.add(wa({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(wa({role:"user",sourceId:r.payload.sourceId})),n.keys.add(wa({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(Hk({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(Wk({id:r.payload.id,box:r.boxId}));return xf.set(e.id,n),n}).finally(()=>Pc.delete(e.id))),Pc.get(e.id))}async function qk(e,t){try{let n=(await vf(e)).keys;for(let r of t||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=wa(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?Cc(r.text):null;if(s){await I(e,{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 jk(s,ga),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}let a=Cw(r);if(a){await I(e,{type:"provider.error",actor:"system",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{cls:a.cls,code:a.code||void 0,status:a.status||void 0,requestId:a.requestId||void 0,text:r.text,ts:r.ts,model:r.model||null,boxRole:r.boxRole,sourceId:r.sourceId||void 0}}),Uk(e,{provider:r.agent||null,cls:a.cls,status:a.status,text:r.text}).catch(()=>{});continue}if(await I(e,{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===e.leadBox&&e.workspace?.branch){let i=Ib(r.text);if(i){let c=Ob(i),l;if(e.workspace?.kind==="multi"){let m=await Go(e).catch(()=>[]);l=m.length?m.map(h=>({name:h.name,slug:h.repo??null})):void 0}let u=(e.workspace?.kind==="multi"?null:await Wo(e.cwd).catch(()=>null))||e.workspace.branch,f=await yk(e,{branch:u,rebase:c}).catch(()=>null),d=f?null:$b(i);if(f&&f.available.length===0)continue;await I(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:xC(),ts:r.ts,excerpt:i,branch:u,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,available:f?.available,defaults:f?.defaults,prState:f?.prState,openPr:f?.openPr||void 0,repos:f?.repos||l,offered:f?.defaults||d?.offered||void 0,toPr:d?.toPr||void 0}})}}}}catch{}}async function Kk(e,t){try{let n=await vf(e),r=[...t||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Hk(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await I(e,{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}}),Nk(e,o),Pk(e,o))}}catch{}}async function Gk(e,t){try{let n=await vf(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Wk(o);n.keys.has(s)||(n.keys.add(s),await I(e,{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 xf,Pc,Vk=O(()=>{le();Bo();bk();Bk();Ko();gf();Ur();Fi();kf();xf=new Map,Pc=new Map});function vC(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
144
|
+
`)){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;t.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 t}async function SC(e,t){let n=`${e.id}:${t.name}`;if(!Sf.has(n)){let r=await B(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);Sf.set(n,o)}return Sf.get(n)}async function zk(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await xr(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await SC(e,t),o=vC(await et(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||t.model||"gpt-5.5";await I(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Fn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await I(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.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 Sf,Jk=O(()=>{le();Sr();vt();ea();Sf=new Map});import Bc from"node:fs";import Af from"node:path";import TC from"node:os";async function _C({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(EC,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${t}`},body:JSON.stringify({teamId:0,startDate:String(e),endDate:String(Date.now()),page:o,pageSize:Yk})});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<Yk)break}return r}function IC(e){if(!e)return null;let t=Number(e.timestamp)||null,n=e.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=e.conversationId||null;return{id:[o||"?",t??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:t,model:e.model||e.modelIntent||null,kind:e.kindLabel||e.kind||null,isHeadless:e.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:e.chargedCents??null}}function Zk(){try{return JSON.parse(Bc.readFileSync(Dc(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function $C(e){try{Bc.mkdirSync(Af.dirname(Dc()),{recursive:!0});let t=Dc()+".tmp";Bc.writeFileSync(t,JSON.stringify(e)),Bc.renameSync(t,Dc())}catch{}}async function CC({fetchImpl:e,now:t=Date.now()}={}){if(Zn.rows&&t-Zn.at<RC)return Zn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Zk(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await _C({sinceMs:o,cookie:n,fetchImpl:e});Zn={at:t,rows:s.map(IC).filter(Boolean),error:null}}catch(s){Zn={at:t,rows:null,error:String(s?.message||s)}}return Zn.rows}async function MC(e,t){let n=`${e.id}:${t.name}`;if(!Tf.has(n)){let r=await B(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);Tf.set(n,o)}return Tf.get(n)}async function Qk(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!Xk())return;let r=await CC({fetchImpl:n});if(!r||!r.length)return;let o=await xy(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await MC(e,t),a=Zk(),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(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.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=Fn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await I(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.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}})}$C({sinceMs:c,seen:[...i].slice(-OC)})}catch{}}var AC,Xk,EC,Yk,RC,Zn,Dc,OC,Ef,Tf,_f=O(()=>{le();bd();ea();AC=process.env.BOSTRAT_STATE_DIR||Af.join(process.env.HOME||TC.tmpdir(),".bostrat"),Xk=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,EC="https://cursor.com/api/dashboard/get-filtered-usage-events",Yk=100;RC=5*6e4,Zn={at:0,rows:null,error:null},Dc=()=>Af.join(AC,"cursor-usage.json");OC=5e3;Ef=()=>({enabled:Xk(),lastPollAt:Zn.at||null,error:Zn.error}),Tf=new Map});import ka from"node:fs/promises";import rt from"node:path";import NC from"node:os";import PC from"node:net";import{randomUUID as BC}from"node:crypto";import{execFile as DC}from"node:child_process";import{promisify as jC}from"node:util";function KC(e){for(let t of Nf(e))if(!sx.test(t))return t;return null}function ax(e){let t=Nf(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function GC(e,t){let n=ax(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return rt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return rt.resolve(e,n[r+1])}return e}function zC(e){let t=!1,n=!1;for(let r of Nf(e)){if(!t){if(sx.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&If(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,If(r))return`serves the filesystem root ("${r}")`;continue}if(If(r))return`serves the filesystem root ("${r}")`;if(VC(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function YC(e,t){if(rt.resolve(e)===rt.resolve(t))return!0;let[n,r]=await Promise.all([jc(e),jc(t)]);return n===r}async function XC(e){let n=(await ka.readdir(e,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await ix(rt.join(e,o.name,Of))?`./${o.name}`:null))).filter(Boolean)}async function ZC(e,t){let[n,r]=await Promise.all([jc(e),jc(t)]),o=rt.relative(n,r);if(o&&(o.startsWith("..")||rt.isAbsolute(o)))throw ya(`serve directory escapes the Track workspace: ${t}`)}async function QC(e,t){let n=zC(t);if(n)throw ya(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await JC(e))throw ya(`serve directory does not exist: ${e}`);let r=KC(t);if(!r)return;if(!r.includes("/")&&!await aw(r,{loginShell:!0,failOpen:!0}))throw ya(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(HC.test(t)||WC.has(r)||!UC.has(r)||!ax(t).includes("run")||qC.test(t))return;let o=GC(e,t);if(await ix(rt.join(o,Of)))return;let s=await XC(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw ya(`no ${Of} in ${o}${a}`)}async function un(){return Dt||(Rf||=(async()=>{try{Dt=JSON.parse(await ka.readFile(ox,"utf8"))}catch{Dt={views:[],adoptWatermarks:{}}}finally{Rf=null}return Dt.views||=[],Dt.adoptWatermarks||={},Dt})(),Rf)}async function Lt(){await Qe(ox,Dt)}function ln(e,t,n={}){return I({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function Bf(e){return ba.get(e)||null}function lx(e,{name:t,box:n}={}){let r=ba.get(e);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:e,name:t||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return ba.set(e,o),o}function Cf(e,t){let n=ba.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Lc(e,{error:t=null}={}){let n=ba.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function ux(e){let t=[];for(let n of e?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||t.push({box:n.box,from:n.startedAt,to:n.endedAt});return t}function Mf(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function eM(e,t){let n=Mf(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!nx.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!nx.has(s))return s}}function xa(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=PC.connect({port:e,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(t,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function Df(e){if(process.platform!=="darwin"){await jt("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await jt("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${e}`]))}catch{return}for(let n of t.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function dx(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function tM(e,t){let n=new Set(e.views.map(o=>o.port)),r=dx();if(t){let o=Number(t);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=ex;o<=tx;o++)if(!(n.has(o)||r.has(o))&&!await xa(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${ex}-${tx}`)}async function nM(e){try{let{stdout:t}=await jt(L,U("list-sessions","-F","#{session_name}"));return t.split(`
|
|
145
|
+
`).some(n=>n.trim()===Vo(e))}catch{return!1}}async function va(e){try{await jt(L,U("kill-session","-t",Vo(e)))}catch{}}async function jf(e){if(!await Ds())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Vo(e.slug),n=e.serveCwd??e.cwd,r=rt.dirname(process.execPath);await jt(L,U("new-session","-d","-s",t,"-c",n)),await jt(L,U("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await jt(L,U("send-keys","-t",t,"Enter")),await jt(L,U("send-keys","-t",t,"-l",e.cmd)),await jt(L,U("send-keys","-t",t,"Enter"))}async function rx(e,t=12){try{let{stdout:n}=await jt(L,U("capture-pane","-t",Vo(e),"-p"));return String(n||"").trimEnd().split(`
|
|
146
|
+
`).filter(Boolean).slice(-t).join(`
|
|
147
|
+
`)}catch{return""}}async function oM(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await nM(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await rx(e.slug)||"server exited before listening"),cn.delete(e.id),await ln(e,e.state,e.error?{error:e.error}:{}),!0}if(await xa(e.port))return cn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await ln(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>rM?(e.state="error",e.error=await rx(e.slug)||"never started listening",await ln(e,"error",{error:e.error}),!0):!1;let o=(cn.get(e.id)||0)+1;return cn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Fc(){if(!$f){$f=!0;try{let e=await un(),t=!1;for(let n of e.views)await oM(n)&&(t=!0);t&&await Lt()}finally{$f=!1}}}async function Lf({trackId:e}={}){await Fc();let t=await un();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function fx(e){let n=(await un()).views.find(r=>r.id===e);return n?{...n}:null}function Ff(e){return Dt&&Dt.views.find(t=>t.slug===e)||null}async function Uf({trackId:e,name:t,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.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=rt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await ZC(r,u),await QC(u,n)},d=await un();if(i!=="detected"){let w=Mf(t),y=String(n).trim(),b=d.views.find(x=>x.trackId===e&&(x.slug===w||Mf(x.name)===w||x.cmd===y&&rt.resolve(x.serveCwd??x.cwd)===u));if(b)return sM(d,b,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=Qn)throw new Error(`view cap reached (${Qn} live) \u2014 stop one first`);let h={id:BC().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:eM(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await tM(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await jf(h),d.views.push(h),await Lt(),await ln(h,"created",{origin:i}),Cf(e,h.id),i==="detected"&&await xa(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await Lt(),await ln(h,"live")),{...h}}async function sM(e,t,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(t.box=r),o&&(t.branch=o);let i=t.cmd!==n,c=s!==void 0&&!await YC(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await xa(t.port))return await Lt(),Cf(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(f=>f.id!==t.id&&(f.state==="live"||f.state==="starting")).length>=Qn)throw new Error(`view cap reached (${Qn} live) \u2014 stop one first`);return t.origin==="detected"?(await Df(t.port),t.origin="agent"):await va(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),cn.delete(t.id),await jf(t),await Lt(),await ln(t,"created",{restarted:!0,reused:!0}),Cf(t.trackId,t.id),{...t,reused:!0}}async function px(e){let n=(await un()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Df(n.port):await va(n.slug),n.state!=="stopped"&&(n.state="stopped",cn.delete(n.id),await Lt(),await ln(n,"stopped")),{...n}}async function mx(e){let t=await un(),n=t.views.find(o=>o.id===e);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(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=Qn)throw new Error(`view cap reached (${Qn} live) \u2014 stop one first`);return await va(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),cn.delete(n.id),await jf(n),await Lt(),await ln(n,"created",{restarted:!0}),{...n}}async function hx(e){let t=await un(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await Df(r.port):await va(r.slug),t.views.splice(n,1),cn.delete(r.id),await Lt(),await ln(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function wx(e){let t=await un(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await va(r.slug),cn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Lt()),n.length}async function gx(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await un(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await Lt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(aM)){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(!dx().has(c)&&!n.views.some(l=>l.port===c)&&await xa(c))try{i.push(await Uf({trackId:e.id,name:`${e.name} preview`,cmd:null,cwd:e.cwd,port:c,origin:"detected",branch:e.workspace?.branch??null}))}catch{}return i}var jt,LC,ox,ex,tx,Qn,FC,Vo,nx,ya,UC,Of,HC,WC,qC,Nf,sx,If,VC,JC,ix,jc,Dt,Rf,ba,Pf,cx,cn,rM,$f,aM,Uc=O(()=>{ze();le();Rn();jt=jC(DC),LC=process.env.BOSTRAT_STATE_DIR||rt.join(process.env.HOME||NC.tmpdir(),".bostrat"),ox=rt.join(LC,"views.json"),[ex,tx]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Qn=Number(process.env.BOSTRAT_VIEW_MAX)||3,FC="bostrat-view-",Vo=e=>`${FC}${e}`,nx=new Set(["bostrat","www","api","views","app"]),ya=e=>Object.assign(new Error(e),{status:400}),UC=new Set(["npm","pnpm","yarn","bun"]),Of="package.json",HC=/[|;&`]|\$\(/,WC=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),qC=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Nf=e=>String(e).trim().split(/\s+/).filter(Boolean),sx=/^[A-Za-z_][A-Za-z0-9_]*=/;If=e=>e==="/",VC=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");JC=e=>ka.stat(e).then(t=>t.isDirectory(),()=>!1),ix=e=>ka.stat(e).then(t=>t.isFile(),()=>!1),jc=e=>ka.realpath(e).catch(()=>rt.resolve(e));Dt=null,Rf=null;ba=new Map,Pf=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,cx=12e3;cn=new Map,rM=12e4;$f=!1;aM=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Hc(e,t=Date.now()){let n=null;for(let r of e||[]){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&&t-n.ts>cM?null:n}function lM(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function yx(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await B(e)}catch{return}let r=Hc(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await Fr(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Hf.get(e.id)||0)<iM)return;Hf.set(e.id,s);let a;try{a=await Nt(e.cwd,{caller:"reconcile"})}catch{return}let i=await qo(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await de(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await it({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!f&&!(u?.trackId===e.id&&await tn(l,c.number))){if(!lM(u,r))return;let d=await B(e).catch(()=>null);if(d&&Hc(d)==null)return;await I(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await I(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),Hf.delete(e.id)}var iM,Hf,cM,Wf=O(()=>{le();Ho();hr();Vn();ha();Ko();iM=6e3,Hf=new Map,cM=30*6e4});import qf from"node:os";import{execFile as uM}from"node:child_process";import{promisify as dM}from"node:util";async function Wc(){if(process.platform!=="darwin")return qf.freemem();try{let{stdout:e}=await fM("vm_stat",[],{timeout:3e3}),t=Number(/page size of (\d+) bytes/.exec(e)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(e)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*t;return r>0?r:qf.freemem()}catch{return qf.freemem()}}var fM,Kf=O(()=>{fM=dM(uM)});import Me from"node:fs/promises";import Pe from"node:path";import xx from"node:os";import{randomUUID as pM}from"node:crypto";import{execFile as mM}from"node:child_process";import{promisify as hM}from"node:util";import{chromium as wM}from"playwright-core";async function Ht(){return Hr||(Gf||=(async()=>{try{Hr=JSON.parse(await Me.readFile(Sx,"utf8"))}catch{Hr={mocks:[]}}finally{Gf=null}return Hr.mocks||=[],Hr})(),Gf)}async function ht(){await Qe(Sx,Hr)}function Ex(e,t){Ut(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Ut(e,t.v,"png")}function Ye(e,t,n={}){return I({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function wt(e,t){let n=Ta(e.cwd);return{html:Pe.join(n,`${e.id}.v${t}.html`),png:Pe.join(n,`${e.id}.v${t}.png`)}}function Sa(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?Pe.join(e.cwd,r):null}function Et(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function Jf(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function qc(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(AM)}function EM(e){return!Buffer.isBuffer(e)||e.length<32?null:e.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:e.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function _M(e){let t=Buffer.alloc(24),n=await Me.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function IM({imgW:e,imgH:t,bbox:n,pad:r,align:o=1}){if(!n||!(e>0)||!(t>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=e*.9&&a>=t*.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 RM(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((h,w)=>{r.onload=h,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,m=-1;for(let h=0;h<s;h++)for(let w=h*o*4,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),h<f&&(f=h),m=h);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:m}}},t.toString("base64"))}async function $M(e,t,{bg:n,crop:r}){let o=await e.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:t.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function OM({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await wM.launch({executablePath:yM,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new At("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:zo})).newPage();a.setDefaultTimeout(Jo),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Jo}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Jo});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await RM(a,i),u=l&&IM({...l,pad:xM*zo,align:zo});u&&(i=await $M(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Tx)throw new At(`capture produced a ${i.length}-byte image`);let c=Jf(i);if(!c)throw new At("output is not a PNG");return await Me.writeFile(t,i),{width:Math.round(c.width/zo),height:Math.round(c.height/zo),dpr:zo,renderMs:Date.now()-r}}catch(s){throw s instanceof At?s:new At(/timeout/i.test(s?.message||"")?`render timed out after ${Jo}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function kx({htmlPath:e,outPath:t,width:n}){let r=await Me.mkdtemp(Pe.join(xx.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await zf(bM,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Jo,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new At(c?.killed?`render timed out after ${Jo}ms`:"firefox exited non-zero",s)}let a=await Me.stat(t).catch(()=>null);if(!a||a.size<Tx)throw new At(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Jf(await _M(t));if(!i)throw new At("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Me.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function CM(e){if(gM==="firefox")return kx(e);try{return await OM(e)}catch(t){if(/failed to launch/i.test(t?.message||""))return kx(e);throw t}}function _x(e){return Vf=Vf.then(e,e),Vf}async function Ix(e,t){let n=await Ht(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await ht(),await Ye(r,"rendering",{version:t});try{let o=await Wc();if(o<vM)throw new At(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=wt(r,t),a=await MM({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Me.rm(s.png,{force:!0}).catch(()=>{});return}Yf(r,{v:t,source:"html",htmlRel:Pe.relative(r.cwd,s.html),pngRel:Pe.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await ht(),await Ye(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Ft(r),Ut(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await ht(),await Ye(r,"error",{version:t,error:r.error}),Ft(r)}}}function Yf(e,t){e.versions=(e.versions||[]).filter(r=>r.v!==t.v).concat(t),e.versions.sort((r,o)=>r.v-o.v);let n=e.versions[e.versions.length-1];e.readyVersion=n.v,e.height=n.height,e.dpr=n.dpr,e.renderMs=n.renderMs??null,n.v===e.version&&(e.state="ready",e.error=null)}function NM({trackId:e,name:t,cwd:n,width:r}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?kM:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Aa({trackId:e,group:t}={}){let n=await Ht(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function dn(e){let n=(await Ht()).mocks.find(r=>r.id===e);return n?{...n}:null}function Rx({html:e,image:t,video:n}){if([typeof e=="string"&&e.trim(),!!t,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(t&&!qc(t))throw new Error("image must be a PNG");if(n){let o=EM(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>bx)throw new Error(`video must be ${bx/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function $x({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=NM({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),m=Rx({html:n,image:r,video:o}),h=await Ht(),w={id:pM().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.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 Me.mkdir(Ta(s),{recursive:!0}),h.mocks.push(w),d?(await Me.writeFile(wt(w,1).html,n),await ht(),await Ye(w,"created"),Ft(w),Ut(w,1,"html"),_x(()=>Ix(w.id,1))):o?(await Cx(w,1,o,m),await ht(),await Ye(w,"created"),await Ye(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),Ft(w),Ex(w,w.versions[0])):(await Mx(w,1,r),await ht(),await Ye(w,"created"),await Ye(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),Ft(w),Ut(w,1,"png")),{...w}}async function Ox(e,{html:t,image:n,video:r,width:o}={}){let a=(await Ht()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=Rx({html:t,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 Me.mkdir(Ta(a.cwd),{recursive:!0}),i?(a.state="queued",await Me.writeFile(wt(a,l).html,t),await ht(),await Ye(a,"updated",{version:l}),Ft(a),Ut(a,l,"html"),_x(()=>Ix(a.id,l))):r?(await Cx(a,l,r,c),await ht(),await Ye(a,"updated",{version:l}),await Ye(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:Et(a)?.durationS??null}),Ft(a),Ex(a,a.versions.find(u=>u.v===l))):(await Mx(a,l,n),await ht(),await Ye(a,"updated",{version:l}),await Ye(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Ft(a),Ut(a,l,"png")),{...a}}async function PM(e){try{let{stdout:t}=await zf(SM,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:Ax,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(t),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 BM(e,t){try{await zf(TM,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:Ax,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Me.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Cx(e,t,n,r){let o=Ta(e.cwd),s=Pe.join(o,`${e.id}.v${t}.${r.ext}`),a=Pe.join(o,`${e.id}.v${t}.poster.png`);await Me.writeFile(s,n);let i=await PM(s),c=await BM(s,a);Yf(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Pe.relative(e.cwd,s),posterRel:c?Pe.relative(e.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&&(e.width=i.width)}async function Mx(e,t,n){let r=Jf(n);if(!r)throw new Error("image must be a PNG");let o=wt(e,t);await Me.writeFile(o.png,n),Yf(e,{v:t,source:"image",htmlRel:null,pngRel:Pe.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function Nx(e){let t=await Ht(),n=t.mocks.find(o=>o.id===e);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?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Ye(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Px(e){let t=await Ht(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=wt(r,o);await Me.rm(s.html,{force:!0}).catch(()=>{}),await Me.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Me.rm(Pe.join(r.cwd,s),{force:!0}).catch(()=>{});return await ht(),await Ye(r,"deleted"),kt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function Bx(e){let n=(await Ht()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Pe.join(vx,"mock-archive",e.id),o=Ta(e.cwd),s=Pe.join(r,".bostrat","mocks");await Me.mkdir(s,{recursive:!0});let a=await Me.readdir(o).catch(()=>[]);for(let i of a)await Me.copyFile(Pe.join(o,i),Pe.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 Me.stat(Pe.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=e.name||e.id,i.updatedAt=Date.now(),Ft(i)}return await ht(),n.length}async function Dx(e){let t=await Ht(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await ht();for(let r of n)kt("mocks",r.id).catch(()=>{})}return n.length}async function jx(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ht();for(let r of n.mocks){await Ft(r);for(let o of r.versions||[])o.htmlRel&&await Ut(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Ut(r,o.v,"png"),o.videoRel&&await Ut(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var zf,vx,Sx,gM,yM,bM,kM,zo,xM,Tx,Jo,vM,bx,SM,TM,Ax,At,Hr,Gf,Ft,Ut,Ta,AM,MM,Vf,Yo=O(()=>{le();Rn();Kf();On();zf=hM(mM),vx=process.env.BOSTRAT_STATE_DIR||Pe.join(process.env.HOME||xx.tmpdir(),".bostrat"),Sx=Pe.join(vx,"mocks.json"),gM=process.env.BOSTRAT_MOCK_ENGINE||"chromium",yM=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",bM=process.env.BOSTRAT_FIREFOX_BIN||"firefox",kM=1300,zo=2,xM=32,Tx=1500,Jo=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,vM=700*1024*1024,bx=100*1024*1024,SM=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",TM=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",Ax=2e4,At=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
148
148
|
${n.trim().split(`
|
|
149
149
|
`).slice(-6).join(`
|
|
150
|
-
`)}`:e),this.name="MockRenderError"}},Dr=null,Hf=null;De=t=>Kt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),je=(t,e,n)=>{let r=(t.versions||[]).find(s=>s.v===e),o=n==="mp4"||n==="webm"?ha(t,r,"video"):r&&ha(t,r,n)||(n==="html"?me(t,e).html:null);return o?Tn("mocks",t.id,e,n,o).catch(()=>{}):Promise.resolve()};wa=t=>Ot.join(t,".bostrat","mocks");iM=Buffer.from([137,80,78,71,13,10,26,10]);hM=mM,Wf=Promise.resolve()});import bM from"node:fs/promises";import Ox from"node:path";import kM from"node:os";import{randomUUID as Cx}from"node:crypto";async function ln(){return cn||(Vf||=(async()=>{let t=!0;try{cn=JSON.parse(await bM.readFile(Mx,"utf8"))}catch{cn={flows:[]},t=!1}finally{Vf=null}if(cn.flows||=[],!t&&kt()){let{records:e,source:n}=await Mt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&cn.flows.push(r.data);cn.flows.length&&await Wo().catch(()=>{})}}return cn})(),Vf)}async function Wo(){await Xt(Mx,cn)}function zf(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 Nx({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>_x)throw new Error(`steps must be an array of 1-${_x}`);let r=await ga({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>Rx)throw new Error(`${l}: caption over ${Rx} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>$x)throw new Error(`${l}: title over ${$x} chars`);let f=String(c.mock??"").trim().replace(/^mock:/,"");if(!f)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(x=>x.id===f);if(p.length||(p=r.filter(x=>x.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(x=>`mock:${x.id}`).join(", ")}) \u2014 use the id`);let h=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(x=>x.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let y=o.findIndex(x=>x.mockId===h.id),k=y>=0?o.splice(y,1)[0].id:Cx().slice(0,8);s.push({id:k,mockId:h.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 vM(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Ix)throw new Error(`name over ${Ix} chars`);return t.trim()}async function Px({trackId:t}={}){let e=await ln();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function SM(t){let n=(await ln()).flows.find(r=>r.id===t);return n?{...n}:null}async function Bx(t){let e=await SM(t);if(!e)return null;let n=new Map((await ga({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=Te(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 Dx({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=vM(e),i=await ln(),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 Nx({trackId:t,steps:n}),d=Date.now(),f={id:Cx().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 Wo(),await zf(f,"created"),Dc(f),{...f}}async function jx(t,{steps:e,note:n}={}){let o=(await ln()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Nx({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 Wo(),await zf(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Dc(o),{...o}}async function Lx(t){let e=await ln(),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 Wo(),await zf(r,"deleted"),ye("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Fx(t){return(await ln()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function Ux(t){let n=(await ln()).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(),Dc(r);return n.length&&await Wo(),n.length}async function Hx(t){let e=await ln(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await Wo();for(let r of n)ye("flows",r.id).catch(()=>{})}return n.length}async function Wx(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Mt("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await ln();for(let r of n.flows)await Dc(r);return{pushed:n.flows.length,source:"plane"}}var xM,Mx,_x,Ix,Rx,$x,cn,Vf,Dc,Jf=C(()=>{st();Sn();Ho();An();xM=process.env.BOSTRAT_STATE_DIR||Ox.join(process.env.HOME||kM.tmpdir(),".bostrat"),Mx=Ox.join(xM,"flows.json"),_x=20,Ix=80,Rx=500,$x=80,cn=null,Vf=null;Dc=t=>Kt("flows",{...t},{trackId:t.trackId}).catch(()=>{})});import fn from"node:fs/promises";import pn from"node:path";import TM from"node:os";import{randomUUID as AM}from"node:crypto";function ya(t){return EM.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}async function Fe(){return un||(Yf||=(async()=>{let t=!0;try{un=JSON.parse(await fn.readFile(Kx,"utf8"))}catch{un={runbooks:[]},t=!1}finally{Yf=null}return un.runbooks||=[],!t&&kt()&&await _M().catch(()=>{}),un})(),Yf)}async function Lr(){await Xt(Kx,un)}function Lc(t){return ba(Kt("runbooks",{...t},{trackId:t.trackId}))}async function _M(){let{records:t,source:e}=await Mt("runbooks");if(!(e!=="plane"||!t.length)){await fn.mkdir(jc,{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 Is("runbooks",r.id,o.v,"md");typeof s=="string"&&await fn.writeFile(dn(r,o.v),s).catch(()=>{})}un.runbooks.push(r)}un.runbooks.length&&await Lr()}}function qo(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 dn(t,e){return pn.join(jc,`${t.id}.v${e}.md`)}function Gx(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 Vx(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>qx)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${qx/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=ya(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 IM(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function RM({trackId:t,status:e}={}){let n=await Fe(),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 zx({trackId:t,status:e}={}){let n=await Fe(),r=await RM({trackId:t,status:e});if(!kt())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Mt("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 Xf(t){let e=await $M(t);if(e)return e;if(!kt())return null;let{records:n,source:r}=await Mt("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 Jx(t,e){let n=await Is("runbooks",t.id,e,"md");return typeof n=="string"?n:null}async function $M(t){let n=(await Fe()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Yx({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=Vx(r),c=await Fe(),l={id:AM().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 fn.mkdir(jc,{recursive:!0}),await fn.writeFile(dn(l,1),r),l.versions.push({v:1,mdRel:pn.relative(jr,dn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Lr(),await qo(l,"created",{version:1}),Lc(l),ba(Tn("runbooks",l.id,1,"md",dn(l,1))),{...l}}async function Xx(t,{md:e,note:n}={}){let o=(await Fe()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");IM(o,"update");let s=Vx(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await fn.mkdir(jc,{recursive:!0}),await fn.writeFile(dn(o,a),e),o.versions.push({v:a,mdRel:pn.relative(jr,dn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Lr(),await qo(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Lc(o),ba(Tn("runbooks",o.id,a,"md",dn(o,a))),{...o}}async function Zx(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Fe()).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 Lr(),await qo(r,e==="done"?"done":"reopened"),Lc(r),{...r})}async function Qx(t){let e=await Fe(),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 fn.rm(pn.join(jr,o.mdRel),{force:!0}).catch(()=>{});return await Lr(),r.trackDeletedAt||await qo(r,"deleted"),ba(ye("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function t0(t){let n=(await Fe()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await qo(r,"orphaned"),r.cwd=null,Lc(r);return n.length&&await Lr(),n.length}async function e0(t){let e=await Fe(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await qo(r,"deleted");for(let o of r.versions||[])await fn.rm(pn.join(jr,o.mdRel),{force:!0}).catch(()=>{});ba(ye("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Lr()),n.length}async function n0(){if(!kt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Mt("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Fe();for(let r of n.runbooks){await Kt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Tn("runbooks",r.id,o.v,"md",pn.join(jr,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var jr,Kx,jc,qx,EM,un,Yf,ba,Fc=C(()=>{Sn();st();An();jr=process.env.BOSTRAT_STATE_DIR||pn.join(process.env.HOME||TM.tmpdir(),".bostrat"),Kx=pn.join(jr,"runbooks.json"),jc=pn.join(jr,"runbooks"),qx=256*1024,EM=[{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/}];un=null,Yf=null;ba=t=>Promise.resolve(t).catch(()=>{})});import Zf from"node:fs/promises";import ka from"node:path";import{randomUUID as OM}from"node:crypto";function tp(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return CM.has(e)?"image":MM.has(e)?"doc":null}function NM(t){let r=ka.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 s0(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>r0)throw new Error(`file too large \u2014 max ${Math.floor(r0/(1024*1024))} MB`);let r=NM(e),o=ka.extname(r).slice(1),s=tp(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=OM().slice(0,8),i=Qf(t);await Zf.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Zf.writeFile(ka.join(i,c),n,{mode:384}),await Zf.chmod(ka.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${o0}/${c}`,kind:s,size:n.length}}function a0(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}function ep(t){return t.role==="user"&&PM.test((t.text||"").trim())}var o0,Qf,r0,CM,MM,PM,np=C(()=>{o0=".bostrat/uploads",Qf=t=>ka.join(t,o0),r0=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,CM=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),MM=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"]);PM=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Fr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var i0=C(()=>{});import{execFile as BM}from"node:child_process";import{promisify as DM}from"node:util";function rp(t){return`refs/bostrat/wip/${t}`}function FM(t,e=Date.now()){return`refs/bostrat/rescue/${t}-${e}`}function u0(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function UM(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:LM}async function op(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Vn(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Vn(r,["remote","get-url","origin"])}catch{return c0.has(r)||(c0.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 So(r,"bostrat: wip snapshot"),{stdout:s}=await Vn(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Uc.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=rp(n);return await Vn(r,["push","--force","origin",`${o}:${i}`],l0),Uc.set(r,a),await I({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
151
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
152
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
150
|
+
`)}`:t),this.name="MockRenderError"}},Hr=null,Gf=null;Ft=e=>Ve("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Ut=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Sa(e,r,"video"):r&&Sa(e,r,n)||(n==="html"?wt(e,t).html:null);return o?$n("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Ta=e=>Pe.join(e,".bostrat","mocks");AM=Buffer.from([137,80,78,71,13,10,26,10]);MM=CM,Vf=Promise.resolve()});import DM from"node:fs/promises";import Wx from"node:path";import jM from"node:os";import{randomUUID as qx}from"node:crypto";async function pn(){return fn||(Xf||=(async()=>{let e=!0;try{fn=JSON.parse(await DM.readFile(Kx,"utf8"))}catch{fn={flows:[]},e=!1}finally{Xf=null}if(fn.flows||=[],!e&&Te()){let{records:t,source:n}=await Be("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&fn.flows.push(r.data);fn.flows.length&&await Xo().catch(()=>{})}}return fn})(),Xf)}async function Xo(){await Qe(Kx,fn)}function Zf(e,t,n={}){return I({id:e.trackId,cwd:e.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,flowId:e.id,name:e.name,stepCount:e.steps.length,mockIds:[...new Set(e.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Gx({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>Lx)throw new Error(`steps must be an array of 1-${Lx}`);let r=await Aa({trackId:e}),o=[...n],s=[];for(let i=0;i<t.length;i++){let c=t[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>Ux)throw new Error(`${l}: caption over ${Ux} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Hx)throw new Error(`${l}: title over ${Hx} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let m=r.filter(x=>x.id===d);if(m.length||(m=r.filter(x=>x.name.toLowerCase()===d.toLowerCase())),!m.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(m.length>1)throw new Error(`${l}: "${d}" is ambiguous (${m.map(x=>`mock:${x.id}`).join(", ")}) \u2014 use the id`);let h=m[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(x=>x.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let y=o.findIndex(x=>x.mockId===h.id),b=y>=0?o.splice(y,1)[0].id:qx().slice(0,8);s.push({id:b,mockId:h.id,caption:u,title:f,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function FM(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>Fx)throw new Error(`name over ${Fx} chars`);return e.trim()}async function Vx({trackId:e}={}){let t=await pn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function UM(e){let n=(await pn()).flows.find(r=>r.id===e);return n?{...n}:null}async function zx(e){let t=await UM(e);if(!t)return null;let n=new Map((await Aa({trackId:t.trackId})).map(r=>[r.id,r]));return t.steps=t.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=Et(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}}),t}async function Jx({trackId:e,name:t,steps:n,cwd:r,box:o,agent:s}){if(!e)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=FM(t),i=await pn(),c=i.flows.find(m=>m.trackId===e&&m.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await Gx({trackId:e,steps:n}),f=Date.now(),d={id:qx().slice(0,8),trackId:e,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 Xo(),await Zf(d,"created"),Kc(d),{...d}}async function Yx(e,{steps:t,note:n}={}){let o=(await pn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Gx({trackId:o.trackId,steps:t,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 Xo(),await Zf(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Kc(o),{...o}}async function Xx(e){let t=await pn(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await Xo(),await Zf(r,"deleted"),kt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function Zx(e){return(await pn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function Qx(e){let n=(await pn()).flows.filter(r=>r.trackId===e.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=e.name||e.id,r.updatedAt=Date.now(),Kc(r);return n.length&&await Xo(),n.length}async function e0(e){let t=await pn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Xo();for(let r of n)kt("flows",r.id).catch(()=>{})}return n.length}async function t0(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await pn();for(let r of n.flows)await Kc(r);return{pushed:n.flows.length,source:"plane"}}var LM,Kx,Lx,Fx,Ux,Hx,fn,Xf,Kc,Qf=O(()=>{le();Rn();Yo();On();LM=process.env.BOSTRAT_STATE_DIR||Wx.join(process.env.HOME||jM.tmpdir(),".bostrat"),Kx=Wx.join(LM,"flows.json"),Lx=20,Fx=80,Ux=500,Hx=80,fn=null,Xf=null;Kc=e=>Ve("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import wn from"node:fs/promises";import gn from"node:path";import HM from"node:os";import{randomUUID as WM}from"node:crypto";function Ea(e){return qM.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Wt(){return mn||(ep||=(async()=>{let e=!0;try{mn=JSON.parse(await wn.readFile(r0,"utf8"))}catch{mn={runbooks:[]},e=!1}finally{ep=null}return mn.runbooks||=[],!e&&Te()&&await KM().catch(()=>{}),mn})(),ep)}async function qr(){await Qe(r0,mn)}function Vc(e){return _a(Ve("runbooks",{...e},{trackId:e.trackId}))}async function KM(){let{records:e,source:t}=await Be("runbooks");if(!(t!=="plane"||!e.length)){await wn.mkdir(Gc,{recursive:!0});for(let n of e){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await Ps("runbooks",r.id,o.v,"md");typeof s=="string"&&await wn.writeFile(hn(r,o.v),s).catch(()=>{})}mn.runbooks.push(r)}mn.runbooks.length&&await qr()}}function Zo(e,t,n={}){return I({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function hn(e,t){return gn.join(Gc,`${e.id}.v${t}.md`)}function o0(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function s0(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>n0)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${n0/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ea(e);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 t}function GM(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function VM({trackId:e,status:t}={}){let n=await Wt(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function a0({trackId:e,status:t}={}){let n=await Wt(),r=await VM({trackId:e,status:t});if(!Te())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Be("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=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function tp(e){let t=await zM(e);if(t)return t;if(!Te())return null;let{records:n,source:r}=await Be("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function i0(e,t){let n=await Ps("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function zM(e){let n=(await Wt()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function c0({trackId:e,trackName:t,name:n,md:r,cwd:o,box:s,agent:a}){if(!e)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=s0(r),c=await Wt(),l={id:WM().slice(0,8),trackId:e,trackName:t||e,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 wn.mkdir(Gc,{recursive:!0}),await wn.writeFile(hn(l,1),r),l.versions.push({v:1,mdRel:gn.relative(Wr,hn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await qr(),await Zo(l,"created",{version:1}),Vc(l),_a($n("runbooks",l.id,1,"md",hn(l,1))),{...l}}async function l0(e,{md:t,note:n}={}){let o=(await Wt()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");GM(o,"update");let s=s0(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await wn.mkdir(Gc,{recursive:!0}),await wn.writeFile(hn(o,a),t),o.versions.push({v:a,mdRel:gn.relative(Wr,hn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await qr(),await Zo(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Vc(o),_a($n("runbooks",o.id,a,"md",hn(o,a))),{...o}}async function u0(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Wt()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await qr(),await Zo(r,t==="done"?"done":"reopened"),Vc(r),{...r})}async function d0(e){let t=await Wt(),n=t.runbooks.findIndex(o=>o.id===e);if(n===-1)throw new Error("runbook not found");let r=t.runbooks[n];t.runbooks.splice(n,1);for(let o of r.versions||[])await wn.rm(gn.join(Wr,o.mdRel),{force:!0}).catch(()=>{});return await qr(),r.trackDeletedAt||await Zo(r,"deleted"),_a(kt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function f0(e){let n=(await Wt()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Zo(r,"orphaned"),r.cwd=null,Vc(r);return n.length&&await qr(),n.length}async function p0(e){let t=await Wt(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Zo(r,"deleted");for(let o of r.versions||[])await wn.rm(gn.join(Wr,o.mdRel),{force:!0}).catch(()=>{});_a(kt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await qr()),n.length}async function m0(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Wt();for(let r of n.runbooks){await Ve("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await $n("runbooks",r.id,o.v,"md",gn.join(Wr,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Wr,r0,Gc,n0,qM,mn,ep,_a,zc=O(()=>{Rn();le();On();Wr=process.env.BOSTRAT_STATE_DIR||gn.join(process.env.HOME||HM.tmpdir(),".bostrat"),r0=gn.join(Wr,"runbooks.json"),Gc=gn.join(Wr,"runbooks"),n0=256*1024,qM=[{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/}];mn=null,ep=null;_a=e=>Promise.resolve(e).catch(()=>{})});import np from"node:fs/promises";import Ia from"node:path";import{randomUUID as JM}from"node:crypto";function op(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return YM.has(t)?"image":XM.has(t)?"doc":null}function ZM(e){let r=Ia.basename(String(e||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function g0(e,{filename:t,buffer:n}){if(!e)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>h0)throw new Error(`file too large \u2014 max ${Math.floor(h0/(1024*1024))} MB`);let r=ZM(t),o=Ia.extname(r).slice(1),s=op(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=JM().slice(0,8),i=rp(e);await np.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await np.writeFile(Ia.join(i,c),n,{mode:384}),await np.chmod(Ia.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${w0}/${c}`,kind:s,size:n.length}}function y0(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function sp(e){return e.role==="user"&&QM.test((e.text||"").trim())}var w0,rp,h0,YM,XM,QM,ap=O(()=>{w0=".bostrat/uploads",rp=e=>Ia.join(e,w0),h0=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,YM=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),XM=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"]);QM=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Kr(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var b0=O(()=>{});import{execFile as eN}from"node:child_process";import{promisify as tN}from"node:util";function ip(e){return`refs/bostrat/wip/${e}`}function oN(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function v0(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function sN(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:rN}async function cp(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await er(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await er(r,["remote","get-url","origin"])}catch{return k0.has(r)||(k0.add(r),t.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 Ro(r,"bostrat: wip snapshot"),{stdout:s}=await er(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Jc.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=ip(n);return await er(r,["push","--force","origin",`${o}:${i}`],x0),Jc.set(r,a),await I({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
151
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function lp(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await er(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await er(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await Ro(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await er(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Jc.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=oN(n);return await er(r,["push","origin",`${o}:${i}`],x0),Jc.set(r,a),await I({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,rescue:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: rescued stranded work ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: rescue push failed: ${String(o?.message||o).split(`
|
|
152
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function S0({listTracks:e,sweepMs:t=sN(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)i?.archivedAt||i?.kind==="setup"||i?.transferredTo||await cp(i,{log:n})}catch(a){n.log?.(`[wip-sync] sweep failed: ${a?.message||a}`)}finally{r=!1}}},s=setInterval(o,t);return s.unref?.(),o(),{stop:()=>clearInterval(s),cycle:o}}var nN,rN,x0,er,Jc,k0,up=O(()=>{cc();le();nN=tN(eN),rN=12e4,x0=6e4;er=(e,t,n=15e3)=>nN("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Jc=new Map,k0=new Set});import aN from"node:fs";import dp from"node:fs/promises";import iN from"node:path";function dN(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function T0(e,{env:t=process.env,fetchImpl:n=(...o)=>fetch(...o),log:r=console}={}){let o=e?.id;if(!o)return{restored:!1,reason:"no track id"};if(!dN(t))return{restored:!1,reason:"plane not configured"};let s=ko(e),a=iN.join(s,`${o}.ndjson`);try{if(aN.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=be(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let d=new AbortController,m=setTimeout(()=>d.abort(),uN),h;try{h=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${cN}`,{headers:c,signal:d.signal})}finally{clearTimeout(m)}if(h.status===404)return{restored:!1,reason:"unknown to plane"};if(!h.ok)return{restored:!1,reason:`plane ${h.status}`};let w=await h.json();for(let y of w.events||[])l.push(JSON.stringify(y));if(l.length>lN)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 dp.mkdir(s,{recursive:!0});let f=`${a}.pull-${process.pid}-${Date.now()}`;return await dp.writeFile(f,l.join(`
|
|
153
153
|
`)+`
|
|
154
|
-
`),await
|
|
154
|
+
`),await dp.rename(f,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var cN,lN,uN,A0=O(()=>{Ge();le();cN=1e3,lN=5e5,uN=3e4});var sv={};zl(sv,{HANDOFF_LATEST_REL:()=>Gr,KNOWN_AGENTS:()=>ja,addTrackRepo:()=>jp,adoptTrack:()=>Qp,agentAvailability:()=>Qc,announceNodeUpdate:()=>_p,answerFullscreenOffer:()=>Gp,answerPrompt:()=>tl,archiveTrack:()=>Vr,archiveTrackForMergedPr:()=>sn,augmentFirstPromptAfterHandoff:()=>P0,buildAgentCmd:()=>M0,canResumeSubtask:()=>nv,cancelQueued:()=>Ua,cancelQueuedToDraft:()=>Op,cancelStopJob:()=>Ha,cancelSubtask:()=>fl,changedRepos:()=>Go,commitTransferOut:()=>ul,createSetupTrack:()=>Up,createTrack:()=>ol,deleteTrack:()=>Wp,demoteTransferredTracks:()=>Zp,ensureLeadEpochs:()=>cl,forceMergeSubtask:()=>rm,getHandoffJob:()=>cs,getSubtaskJob:()=>pP,getTrack:()=>ee,getTransferOutJob:()=>Jp,interruptLead:()=>rl,isArchiving:()=>rP,isHandoffNoteEcho:()=>xp,isInterruptEcho:()=>vp,isModelSwitchEcho:()=>Sp,listSubtaskJobs:()=>zr,listTracks:()=>ce,matchTranscriptsToBoxes:()=>j0,mergeNotifyMessage:()=>Z0,navigatePrompt:()=>$p,notifyTrackForMergedPr:()=>an,notifyTrackForResolvedPr:()=>sl,queueShipCompleteNotify:()=>Mp,queuedMessages:()=>rs,reapArchivedOrphans:()=>Kp,reconcileHandoffOutcome:()=>Q0,reconcileInterruptedHandoffs:()=>zp,reconcileInterruptedSubtasks:()=>em,reconcileSubtaskOutcome:()=>tv,refreshStandingInstructions:()=>Fp,refuseIfTransferred:()=>Xp,rehydrateLead:()=>al,removeTrackRepo:()=>Lp,reposOf:()=>lt,resolveTrackBox:()=>ga,resolveTrackBoxByHookToken:()=>Ip,restartLead:()=>il,resumeAgentCmd:()=>Ep,sayToLead:()=>yt,sendOptionsForAgent:()=>Mt,setTrackDraft:()=>Bp,setTrackModel:()=>Dp,startLeadHandoff:()=>ll,startSpinUpView:()=>Np,startSubtask:()=>nm,startTransferOut:()=>Yp,stopJobActive:()=>H0,subtaskMessages:()=>Rp,trackMessages:()=>el,unarchiveTrack:()=>qp});import D from"node:fs/promises";import F from"node:path";import{randomUUID as is}from"node:crypto";import{execFile as fN}from"node:child_process";import{promisify as pN}from"node:util";function M0(e,{skipPermissions:t=!0}={}){let n=Zc[e];if(!n)throw new Error(`unknown agent "${e}"`);let r=process.env[n.envVar];return r||(t&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function Ep(e,t,{skipPermissions:n=!0}={}){let r=Zc[e];if(!r)throw new Error(`unknown agent "${e}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(e){case"claude":return`${r.base} --resume ${t}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function La(e){let t=Zc[e];if(t)return process.env[t.envVar]||t.base}async function Qc({maxAgeMs:e=6e4}={}){if(tr.value&&Date.now()-tr.at<e)return tr.value;let t=tr.value,n=await Promise.all(ja.map(async r=>{let o=await Ni(La(r),{retries:[]});return o===lo?[r,t?.[r]??!0]:[r,o===Mi]}));return tr.value=Object.fromEntries(n),tr.at=Date.now(),tr.value}async function Fa(e,t=!0){let n=M0(e,{skipPermissions:t});if(await Ni(n)===co){tr.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.":ku[r]?`Install it on this node: ${ku[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${e}" isn't available on this box (command not found: ${r}). ${o}`)}return n}function P0(e,t=Gr){return`${e.trim()}
|
|
155
155
|
|
|
156
|
-
(Read ${
|
|
157
|
-
`;return lp=lp.then(()=>W.mkdir(hn,{recursive:!0})).then(()=>W.appendFile(YM,n)).catch(()=>{}),lp}function T0(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=>nN.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 A0(t,e,n,{kind:r}={}){let o=await nt(e);if(!o)throw new Error(`box "${e}" is not running`);let s=Oa(o.agent);await On({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await Pt(e,n,{...Oe(o.agent),kind:r||"orchestration"}),await z({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await G(),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 Y()}async function rN(t){let e=q.join(ho(t),`${t.id}.ndjson`),n;try{n=await W.stat(e)}catch{return null}let r=m0.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return m0.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function oN(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Zd(n.name,n.agent)}async function Ue(t,e=null){let n=e??new Map((await be()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=Jn.has(i.state)?i.state:c?i.role==="lead"?"live":i.state:"stopped";return{...i,state:l,cwd:c?.cwd||i.cwd,agent:c?.agent||i.agent,sessionId:c?.sessionId||i.sessionId}}),[o,{working:s,prompted:a}]=await Promise.all([rN(t),oN(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Ia.has(t.id)}}function E0(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-sN,f=r[u+1]?.createdAt??1/0,p=o.find(h=>!s.has(h.id)&&h.birthMs>=d&&h.birthMs<f);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function _0(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=uw(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=E0(u,a,f=>i.has(f));for(let f of d){let p=u.find(h=>h.name===f.box);p&&(p.sessionId=f.sessionId),await ew(f.box,f.sessionId),n=!0}}return n}async function ot(){let t=await G();await _0(t)&&await Y();let e=new Map((await be()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>Ue(n,e)))}async function sa(t,e){let n=await G();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 vp(t){let e=ws(t);if(!e)return null;let n=await G();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&ah(o.hookTokenHash,e))return{track:r,box:o};return null}async function X(t){let e=await G(),n=Z(e,t);return n?(await _0(e)&&await Y(),Ue(n)):null}function gp(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(aN[t.agent]||[]).some(n=>n.test(e))}function yp(t){let e=(t.text||"").trim();return e?iN.some(n=>n.test(e)):!1}async function Gc(t,e={}){let n=await X(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=ts(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=Sf(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Bk(n),...l],f={windows:[...Lg(n),...Fg(n,Vi),...a,...u]},h=(await Promise.all(n.boxes.map(b=>yd(b,e)))).flat(),w=h.map(b=>{let E={kind:"message",...b};if(E.role==="assistant"&&(E.text||"").trim()&&E.tools?.length){let O=Js(E.agent).cliTools,P=E.tools.filter(wt=>!O.has(wt));return{...E,tools:P.length?P:void 0}}return E}).map(b=>{if(b.role!=="user")return b;let E=qi(b.text);return E?E.kind==="suppress"?null:{...b,note:E,text:lg(E)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!gp(b)).filter(b=>!yp(b)).filter(b=>!wp(b)).filter(b=>!ep(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!Ug(b,f)),y=w.filter(b=>b.failed&&b.text===hr);if(y.length){let b=new Map;for(let E of y){if(!b.has(E.box)){let wt=[...(await xt(E.box,{lines:Nt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(mt=>mt[1]);b.set(E.box,wt.length?wt[wt.length-1]:null)}let O=b.get(E.box);O&&(E.text=`\u25A0 ${O}`)}}let k=await Promise.all(n.boxes.map(b=>yy(b,e)));await yk(n,k.flat()),await wk(n,w),await pb(n),await Wk(n,w).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await kk(n,b),b.agent==="cursor"&&b.state==="live"&&await Ak(n,b);let x=await Promise.all(n.boxes.map(b=>gy(b,e)));await gk(n,x.flat().filter(b=>!up(b.box,b.ts,u)));let A=await B(n),R=c&&i.viewId?i.viewId:null,V=Sb(A),M=br(A).filter(b=>!(b.kind==="message"&&(gp(b)||yp(b)||wp(b)||ep(b)))&&!(b.kind==="view-card"&&R&&b.viewId===R)),N=Tb(M,V),T=M.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!N.has(b)&&up(b.box,b.ts,V))),D=Qu(A,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=>!up(b.anchorBox,b.anchorTs??b.ts,u)),F=wg(gg([...T,...D].filter(b=>b.ts!=null).sort((b,E)=>b.ts-E.ts))),ct=lN(n),Q=ct.size?F.filter(b=>b.kind==="event"||!ct.has(b.box??b.anchorBox)):F,_=n.boxes.find(b=>b.name===n.leadBox)||null,U=_?await Cs(_.cwd,_.sessionId,{fallbackNewest:!1}):null;if(!U&&_){let b=[...A].reverse().find(E=>E.type==="context.usage"&&E.boxId===_.name);b?.payload?.window&&(U={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 j=[...h].reverse().find(b=>b.role==="assistant"&&b.model),at=j?.model||null,Et=[...n.events||[]].reverse().find(b=>b.type==="model-change"),Lt=U?.model||at;Et&&(!j?.ts||Et.at>=j.ts)&&(Lt=Et.to);let it=_?await xt(_.name,{lines:Nt}).catch(()=>""):"",g=_&&Er(_.agent,it)?await Ve(_.name).catch(()=>it):it,m=_?Dn(_,g):null;if(m&&_){let b=Zi(_,m,g);if(b){let E=Ny(_.name,m.promptId);E.action!=="give-up"&&(E.action==="send"&&(_t(_.name,b.keys,{kind:"auto-reject"}).catch(()=>{}),E.first&&I(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:_.name,provider:_.agent,payload:{ts:Date.now(),promptId:m.promptId,rule:b.id}}).catch(()=>{})),m=null)}}!m&&_&&!Ls(_.agent,it)&&!Jo(n.id).length&&(m=Xu(Q,{leadBoxId:_.name,provider:_.agent,isAnswered:b=>A.some(E=>E.type==="prompt.answered"&&E.payload?.promptId===b)})),m&&mb(n,_,m);let S=Q.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),lt=!!_&&gc(_.name,_.agent,it)&&!m;_&&!_.readyAt&&(lt||m||it&&Di(_.agent,it))&&(_.readyAt=Date.now(),await Y());let ht=!!_&&!_.readyAt&&Date.now()-(n.createdAt||0)<cN;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&_&&Di(_.agent,it)&&!m){let b=await G(),E=Z(b,n.id);E?.setup&&!E.setup.connectedAt&&(E.setup.connectedAt=Date.now(),n.setup.connectedAt=E.setup.connectedAt,await I(E,{type:"provider.connected",actor:"system",source:"pane",boxId:_.name,provider:_.agent,payload:{provider:_.agent,ts:E.setup.connectedAt}}).catch(()=>{}),await Y())}let vt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Ct=null;if(c){let b=Date.now()-i.startedAt;if(i.viewId&&!lt){let E=Ec(n.id);E&&(pt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:E.finishedAt}),await Y())}else if(m||b>Nk&&!lt&&!i.viewId||b>vf){let E=m?"needs-input":b>vf?"timeout":"no-view";Ec(n.id,{error:E})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:_?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:E}}).catch(()=>{})}else Ct={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:S,context:U?{pct:U.pct,occupancy:U.occupancy,window:U.window,windowKnown:U.windowKnown,model:U.model}:null,models:{current:Lt,options:ab(_?.agent)},pendingPrompt:m,working:lt,stopping:$0(n.id),leadStarting:ht,handoff:vt,view:Ct,queued:Jo(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function lN(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 Sp(t,e){let n=await X(t);if(!n)return null;let r=Wr(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 B(n),a=br(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Qu(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 xt(o,{lines:Nt}).catch(()=>"");u=gc(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Vc(t,{promptId:e,answers:n,text:r}={}){let o=Z(await G(),t);if(!o)throw new Error(`track "${t}" not found`);Na(t,"prompt-answer");let s=o.boxes.find(h=>h.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await nt(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Ad(s);if(i||(i=await uN(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 h=typeof r=="string"?r.trim():"";if(!h)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(h)||h.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Pt(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await _t(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=pg(i,n||[],typeof r=="string"?r:"");if(!h)throw new Error("no answer selected \u2014 pick an option or reply in chat");await we(o.id,h,{kind:"prompt-answer"}),c={mode:"say",value:h,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let h=$y(i,n||[]);await _t(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=jw(i,n||[]);await Pt(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:h,count:1}}if(i.answerMode==="say"){let h=i.questions||[];for(let w=0;w<h.length;w++){let y=h[w],k=new Set(Array.isArray(n?.[w])?n[w]:[]),x=[...k].map(R=>y.options?.[R]).filter(Boolean).map(R=>({label:R.label,description:R.description||""})),A=(y.options||[]).map((R,V)=>k.has(V)?null:R?.label).filter(Boolean);Kd(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??h.length,header:y.header||"",question:y.question||"",multiSelect:!!y.multiSelect,chosen:x,rejected:A})}return await z({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=new Set(Array.isArray(n?.[0])?n[0]:[]),d=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),f=(l.options||[]).map((h,w)=>u.has(w)?null:h?.label).filter(Boolean),p=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:d,rejected:f,...p?{control:p}:{}},await z({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Kd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function uN(t,e){if(!e)return null;let n=await B(t).catch(()=>[]);return Xu(br(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Tp(t,{promptId:e,questionIndex:n}={}){let r=Z(await G(),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 nt(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Ad(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=Oy(a,n);return c.length?(await _t(r.leadBox,c,{kind:"navigate-prompt"}),await z({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 zc(t){let e=Ta.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},Ta.set(t,e)),e}function Jo(t){return(Ta.get(t)?.items??[]).map(e=>({...e}))}function Ma(t){let e=Ta.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 Ap(t){let e=Ma(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=Z(await G(),t);if(o)return r=Yo(o,n),await Y(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function Ep(t){let e=zc(t);e.timer||(e.timer=setInterval(()=>fN(t).catch(()=>{}),dN))}async function fN(t){let e=Ta.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 G(),t);if(!n||n.archivedAt){Ma(t);return}let r=ts(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Fr(e,!1);return}let o=await nt(n.leadBox);if(!o){Fr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(Ir(s?.agent)){Fr(e,!1);return}let a=await xt(n.leadBox,{lines:Nt}).catch(()=>"");if(Bi(s?.agent,a)){Fr(e,!1);return}if(Er(s?.agent,a)){let c=await Ve(n.leadBox).catch(()=>a);if(Dn(s,c)){Fr(e,!1);return}}if(!Fr(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=Ir(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Yo(t,[n]),await Y()),Yy(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 pN(t,e){let n=await xt(t.leadBox,{lines:Nt}).catch(()=>"");if(!Er(e?.agent,n))return!1;let r=await Ve(t.leadBox).catch(()=>n);return!!Dn(e,r)}async function R0(t,e,n,r,o=null,s=null){await I0(t,e,r,o);let a=Aa.get(t.id)||0,i=r,c=!1,l=t.pendingHandoffHydrate||null;t.pendingHandoffHydrate&&(i=S0(r,t.pendingHandoffHydrate.artifact||Ur),delete t.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(u.length){let f=a0(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
156
|
+
(Read ${t} 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 xp(e){return e.role==="user"&&wN.test((e.text||"").trim())}async function G(){return gt||(fp||=(async()=>{try{gt=JSON.parse(await D.readFile(C0,"utf8"))}catch{gt={tracks:[]}}finally{fp=null}return gt})(),fp)}async function Z(){await Qe(C0,gt)}function bN(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
157
|
+
`;return pp=pp.then(()=>D.mkdir(qt,{recursive:!0})).then(()=>D.appendFile(mN,n)).catch(()=>{}),pp}async function _p({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1}={}){if(!t)return{announced:0};let s=null;try{s=JSON.parse(await D.readFile(mp,"utf8"))}catch{}if(s?.latest===t&&s?.current===e)return{announced:0,deduped:!0};let a=await G(),i=0;for(let c of a.tracks)c.archivedAt||!c.leadBox||(pe(c,"node-update",{current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},required:o===!0}),i++);await Z().catch(()=>{});try{await D.mkdir(F.dirname(mp),{recursive:!0}),await D.writeFile(mp,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:i}}function B0(e){if(!e)return null;let t=typeof e=="string"?[e]:Array.isArray(e?.codes)&&e.codes.length?e.codes:e?.code?[e.code]:[],n=[...new Set(t.filter(s=>kN.has(s)))];if(!n.length)return null;let r=typeof e=="object"?e?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function D0(e,t,n,{kind:r}={}){let o=await oe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=La(o.agent);await Dn({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await je(t,n,{...Mt(o.agent),kind:r||"orchestration"}),await z({flow:"orchestration",phase:"sent",trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await G(),i=te(a,e);if(!i)return;let c=i.boxes.find(l=>l.name===t);pe(i,"orchestration-prompt",{box:t,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await Z()}async function xN(e){let t=F.join(ko(e),`${e.id}.ndjson`),n;try{n=await D.stat(t)}catch{return null}let r=E0.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return E0.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function vN(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:ff(n.name,n.agent)}async function Kt(e,t=null){let n=t??new Map((await xt()).map(i=>[i.name,i])),r=e.boxes.map(i=>{let c=n.get(i.name),l=nr.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([xN(e),vN(e,r)]);return{...e,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Ba.has(e.id)}}function j0(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].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-SN,d=r[u+1]?.createdAt??1/0,m=o.find(h=>!s.has(h.id)&&h.birthMs>=f&&h.birthMs<d);m&&(s.add(m.id),c.push({box:l.name,sessionId:m.id}))}return c}async function L0(e){let t=new Map;for(let r of e.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(t.has(o.cwd)||t.set(o.cwd,[]),t.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of t){let s=o.map(d=>d.box.sessionId),a=yw(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=j0(u,a,d=>i.has(d));for(let d of f){let m=u.find(h=>h.name===d.box);m&&(m.sessionId=d.sessionId),await lw(d.box,d.sessionId),n=!0}}return n}async function ce(){let e=await G();await L0(e)&&await Z();let t=new Map((await xt()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Kt(n,t)))}async function ga(e,t){let n=await G();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function Ip(e){let t=Ss(e);if(!t)return null;let n=await G();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&mh(o.hookTokenHash,t))return{track:r,box:o};return null}async function ee(e){let t=await G(),n=te(t,e);return n?(await L0(t)&&await Z(),Kt(n)):null}function vp(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(TN[e.agent]||[]).some(n=>n.test(t))}function Sp(e){let t=(e.text||"").trim();return t?AN.some(n=>n.test(t)):!1}async function el(e,t={}){let n=await ee(e);if(!n)return null;let r=new Set([...n.events.filter(v=>v.type==="orchestration-prompt"&&v.prompt).map(v=>String(v.prompt).trim()),...n.events.filter(v=>v.type==="said"&&v.text).map(v=>String(v.text).trim())]),o=cs(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=Bf(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...ux(n),...l],d={windows:[...Vg(n),...zg(n,tc),...a,...u]},h=(await Promise.all(n.boxes.map(v=>Ad(v,t)))).flat(),w=h.map(v=>{let V={kind:"message",...v};if(V.role==="assistant"&&(V.text||"").trim()&&V.tools?.length){let Tn=na(V.agent).cliTools,Y=V.tools.filter(P=>!Tn.has(P));return{...V,tools:Y.length?Y:void 0}}return V}).map(v=>{if(v.role!=="user")return v;let V=Zi(v.text);return V?V.kind==="suppress"?null:{...v,note:V,text:gg(V)}:v}).filter(Boolean).filter(v=>!(v.role==="user"&&r.has((v.text||"").trim()))).filter(v=>!vp(v)).filter(v=>!Sp(v)).filter(v=>!xp(v)).filter(v=>!sp(v)).filter(v=>v.role!=="assistant"||!!(v.text||"").trim()||!!v.images?.length).filter(v=>!Jg(v,d)),y=w.filter(v=>v.failed&&v.text===vr);if(y.length){let v=new Map;for(let V of y){if(!v.has(V.box)){let P=[...(await Ae(V.box,{lines:De}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(R=>R[1]);v.set(V.box,P.length?P[P.length-1]:null)}let Tn=v.get(V.box);Tn&&(V.text=`\u25A0 ${Tn}`)}}let b=await Promise.all(n.boxes.map(v=>Ey(v,t)));await Gk(n,b.flat()),await qk(n,w),await xb(n),await gx(n,w).catch(()=>{});for(let v of n.boxes)v.agent==="codex"&&v.state==="live"&&await zk(n,v),v.agent==="cursor"&&v.state==="live"&&await Qk(n,v);let x=await Promise.all(n.boxes.map(v=>Ay(v,t)));await Kk(n,x.flat().filter(v=>!hp(v.box,v.ts,u)));let E=await B(n),_=c&&i.viewId?i.viewId:null,q=Mb(E),C=Er(E).filter(v=>!(v.kind==="message"&&(vp(v)||Sp(v)||xp(v)||sp(v)))&&!(v.kind==="view-card"&&_&&v.viewId===_)),N=Nb(C,q),T=C.filter(v=>!(v.kind==="message"&&v.role==="assistant"&&!N.has(v)&&hp(v.box,v.ts,q))),W=ad(E,T).map(v=>({kind:"command-batch",id:v.id,ts:v.pending?v.ts:(v.anchorTs??v.ts)-1,anchorTs:v.anchorTs,anchorBox:v.anchorBox,agent:v.agent,boxRole:v.boxRole,commands:v.commands,pending:v.pending})).filter(v=>!hp(v.anchorBox,v.anchorTs??v.ts,u)),H=Tg(Ag([...T,...W].filter(v=>v.ts!=null).sort((v,V)=>v.ts-V.ts))),ae=_N(n),J=ae.size?H.filter(v=>v.kind==="event"||!ae.has(v.box??v.anchorBox)):H,A=n.boxes.find(v=>v.name===n.leadBox)||null,j=A?await Ls(A.cwd,A.sessionId,{fallbackNewest:!1}):null;if(!j&&A){let v=[...E].reverse().find(V=>V.type==="context.usage"&&V.boxId===A.name);v?.payload?.window&&(j={pct:v.payload.pct??(v.payload.occupancy!=null?v.payload.occupancy/v.payload.window:null),occupancy:v.payload.occupancy??null,window:v.payload.window,windowKnown:!0,model:v.payload.model||null})}let Q=[...h].reverse().find(v=>v.role==="assistant"&&v.model),fe=Q?.model||null,Re=[...n.events||[]].reverse().find(v=>v.type==="model-change"),ue=j?.model||fe;Re&&(!Q?.ts||Re.at>=Q.ts)&&(ue=Re.to);let ie=A?await Ae(A.name,{lines:De}).catch(()=>""):"",g=A&&Mr(A.agent,ie)?await Xt(A.name).catch(()=>ie):ie,p=A?Wn(A,g):null;if(p&&A){let v=ac(A,p,g);if(v){let V=Hy(A.name,p.promptId);V.action!=="give-up"&&(V.action==="send"&&(Oe(A.name,v.keys,{kind:"auto-reject"}).catch(()=>{}),V.first&&I(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{ts:Date.now(),promptId:p.promptId,rule:v.id}}).catch(()=>{})),p=null)}}!p&&A&&!Gs(A.agent,ie)&&!rs(n.id).length&&(p=od(J,{leadBoxId:A.name,provider:A.agent,isAnswered:v=>E.some(V=>V.type==="prompt.answered"&&V.payload?.promptId===v)})),p&&vb(n,A,p);let S=J.filter(v=>!(v.kind==="message"&&v.role==="assistant"&&v.ask&&!String(v.text||"").trim()&&!v.images?.length)),we=!!A&&$c(A.name,A.agent,ie)&&!p;A&&!A.readyAt&&(we||p||ie&&Ki(A.agent,ie))&&(A.readyAt=Date.now(),await Z());let ge=!!A&&!A.readyAt&&Date.now()-(n.createdAt||0)<EN;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&A&&Ki(A.agent,ie)&&!p){let v=await G(),V=te(v,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:A.name,provider:A.agent,payload:{provider:A.agent,ts:V.setup.connectedAt}}).catch(()=>{}),await Z())}let Se=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,$e=null;if(c){let v=Date.now()-i.startedAt;if(i.viewId&&!we){let V=Lc(n.id);V&&(pe(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:V.finishedAt}),await Z())}else if(p||v>cx&&!we&&!i.viewId||v>Pf){let V=p?"needs-input":v>Pf?"timeout":"no-view";Lc(n.id,{error:V})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:A?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:V}}).catch(()=>{})}else $e={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:S,context:j?{pct:j.pct,occupancy:j.occupancy,window:j.window,windowKnown:j.windowKnown,model:j.model}:null,models:{current:ue,options:mb(A?.agent)},pendingPrompt:p,working:we,stopping:H0(n.id),leadStarting:ge,handoff:Se,view:$e,queued:rs(n.id).filter(v=>v.kind!=="ship-notify"&&v.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function _N(e){let t=new Set;for(let n of e.boxes||[])n.role==="subtask"&&n.name&&t.add(n.name);for(let n of Object.values(e.subtasks||{}))n?.box&&t.add(n.box);return t}async function Rp(e,t){let n=await ee(e);if(!n)return null;let r=zr(e).find(f=>f.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await B(n),a=Er(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=ad(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 Ae(o,{lines:De}).catch(()=>"");u=$c(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function tl(e,{promptId:t,answers:n,text:r}={}){let o=te(await G(),e);if(!o)throw new Error(`track "${e}" not found`);Ha(e,"prompt-answer");let s=o.boxes.find(h=>h.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await oe(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Cd(s);if(i||(i=await IN(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==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 h=typeof r=="string"?r.trim():"";if(!h)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(h)||h.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await je(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await Oe(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=xg(i,n||[],typeof r=="string"?r:"");if(!h)throw new Error("no answer selected \u2014 pick an option or reply in chat");await yt(o.id,h,{kind:"prompt-answer"}),c={mode:"say",value:h,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let h=jy(i,n||[]);await Oe(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=Gw(i,n||[]);await je(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:h,count:1}}if(i.answerMode==="say"){let h=i.questions||[];for(let w=0;w<h.length;w++){let y=h[w],b=new Set(Array.isArray(n?.[w])?n[w]:[]),x=[...b].map(_=>y.options?.[_]).filter(Boolean).map(_=>({label:_.label,description:_.description||""})),E=(y.options||[]).map((_,q)=>b.has(q)?null:_?.label).filter(Boolean);Jd(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??h.length,header:y.header||"",question:y.question||"",multiSelect:!!y.multiSelect,chosen:x,rejected:E})}return await z({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=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),d=(l.options||[]).map((h,w)=>u.has(w)?null:h?.label).filter(Boolean),m=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:f,rejected:d,...m?{control:m}:{}},await z({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Jd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function IN(e,t){if(!t)return null;let n=await B(e).catch(()=>[]);return od(Er(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function $p(e,{promptId:t,questionIndex:n}={}){let r=te(await G(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await oe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Cd(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(t&&t!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=Ly(a,n);return c.length?(await Oe(r.leadBox,c,{kind:"navigate-prompt"}),await z({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 nl(e){let t=Ca.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ca.set(e,t)),t}function rs(e){return(Ca.get(e)?.items??[]).map(t=>({...t}))}function Ua(e){let t=Ca.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function Op(e){let t=Ua(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=te(await G(),e);if(o)return r=os(o,n),await Z(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Cp(e){let t=nl(e);t.timer||(t.timer=setInterval(()=>$N(e).catch(()=>{}),RN))}async function $N(e){let t=Ca.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=te(await G(),e);if(!n||n.archivedAt){Ua(e);return}let r=cs(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Kr(t,!1);return}let o=await oe(n.leadBox);if(!o){Kr(t,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(Pr(s?.agent)){Kr(t,!1);return}let a=await Ae(n.leadBox,{lines:De}).catch(()=>"");if(qi(s?.agent,a)){Kr(t,!1);return}if(Mr(s?.agent,a)){let c=await Xt(n.leadBox).catch(()=>a);if(Wn(s,c)){Kr(t,!1);return}}if(!Kr(t,!0))return;let i=t.items.shift();t.idleSince=null,await U0(n,s,o,i.text,i.kind??null,i.attachments??null),await Z()}finally{t.flushing=!1}}}async function F0(e,t,n,r){let o=Pr(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(os(e,[n]),await Z()),ob(e.id,o))try{await I(e,{type:"usage.cap.hit",actor:"system",provider:t?.agent??null,boxId:e.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${t?.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 ON(e,t){let n=await Ae(e.leadBox,{lines:De}).catch(()=>"");if(!Mr(t?.agent,n))return!1;let r=await Xt(e.leadBox).catch(()=>n);return!!Wn(t,r)}async function U0(e,t,n,r,o=null,s=null){await F0(e,t,r,o);let a=Ma.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=P0(r,e.pendingHandoffHydrate.artifact||Gr),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=y0(u.map(m=>m.rel));i=i.trim()?`${i.trim()}
|
|
158
158
|
|
|
159
|
-
${
|
|
159
|
+
${d}`:d}let f=pe(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});ns(e,null);try{if(await Dn({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:La(n.agent),bailOnDialog:!0}),(Ma.get(e.id)||0)!==a){!o&&r.trim()&&(os(e,[r]),await Z());let d=new Error("delivery stopped \u2014 the message was handed back as the draft");throw d.interrupted=!0,d}if(await ON(e,t)){l&&(e.pendingHandoffHydrate=l);let d=nl(e.id),m={id:is(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};d.items.unshift(m),Cp(e.id);let h=new Error("lead is blocked on a prompt \u2014 message queued");throw h.requeued={id:m.id,position:d.items.length},h}await je(e.leadBox,i,{...Mt(t?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(d){if(pe(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,...d?.interrupted?{reason:"interrupted"}:{},...d?.requeued?{reason:"requeued"}:{}}),d?.requeued)return{queued:!0,...d.requeued};throw d}await GN(e,t,i)||(await W0(e.leadBox,t?.agent,i.length+8).catch(()=>{}),pe(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,reason:"unsubmitted"}),o||os(e,[r])),c&&await z({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Mp(e,{actions:t}={}){nl(e).items.push({id:is(),text:Pb(t),ts:Date.now(),kind:"ship-notify"}),Cp(e)}async function yt(e,t,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(m=>m&&typeof m.rel=="string"):[];if((typeof t!="string"||!_o(t))&&!o.length)throw new Error("text (string) or an attachment required");let s=te(await G(),e);if(!s)throw new Error(`track "${e}" not found`);if(s.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);Xp(s);let a=s.boxes.find(m=>m.name===s.leadBox),i=_o(t);await F0(s,a,i,n),Ha(e,"say");let c=await oe(s.leadBox);if(!c&&await al(s)&&(await Z(),c=await oe(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=nl(e),u=await Ae(s.leadBox,{lines:De}).catch(()=>""),f=l.items.length>0||qi(a?.agent,u);if(!f&&Mr(a?.agent,u)){let m=await Xt(s.leadBox).catch(()=>u);f=!!Wn(a,m)}if(f){let m={id:is(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(m),Cp(e),ns(s,null),await Z(),{ok:!0,leadBox:s.leadBox,queued:!0,id:m.id,position:l.items.length}}let d=await U0(s,a,c,i,n,o);return await Z(),d?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:d.position}:{ok:!0,leadBox:s.leadBox}}async function Np(e,{hint:t}={}){let n=te(await G(),e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it first`);let r=Bf(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(t||"").trim()?String(t).trim().slice(0,40):"preview";lx(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${_0}
|
|
160
160
|
|
|
161
|
-
What to preview: ${String(
|
|
161
|
+
What to preview: ${String(t).trim()}`:_0;try{await yt(e,a,{kind:"spin-up-view"})}catch(i){throw Lc(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function rl(e){let t=te(await G(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);Ma.set(e,(Ma.get(e)||0)+1);let n=Ua(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=ts.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=os(t,r),await Z()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=DN(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,rr(CN)]);let a=te(await G(),e)||t;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 H0(e){let t=ts.get(e);return!!t&&!t.canceled}function Ha(e,t){let n=ts.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function Pp(e){Ha(e,"track-ended"),Ma.delete(e),Ua(e)}function DN(e,{droppedCount:t,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}),ts.set(e,r),(async()=>{let s=te(await G(),e),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await oe(s.leadBox):null,c=!1,l=!1;if(i){e:for(let y=0;y<MN&&!r.canceled;y++){await Oe(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let b of NN){await rr(b);let x=await Ae(s.leadBox,{lines:De}).catch(()=>"");if(!Gs(a?.agent,x)){c=!0;break e}}}if(!c){l=!0;for(let y=0;y<BN&&!r.canceled&&(await rr(PN),!r.canceled);y++){let b=await Ae(s.leadBox,{lines:De}).catch(()=>"");if(!Gs(a?.agent,b)){c=!0;break}await Oe(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=te(await G(),e)||s,f=u?.boxes.find(y=>y.name===u.leadBox)||a,d=null,m=null;u&&f&&i&&c&&!r.canceled&&(d=await UN(u,f).catch(()=>null),d||(m=await qN(u,f).catch(()=>null)));let h=u&&i&&!r.canceled?await LN(u).catch(()=>null):null,w=[d||m,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=os(u,w),await Z()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await jN(u||s,f,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!m,queuedRestored:r.queuedTexts.length,shipDismissed:!!h,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await z({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!m,queuedRestored:r.queuedTexts.length,shipDismissed:!!h}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),ts.get(e)===r&&ts.delete(e),o()}),r}async function jN(e,t,n){await I(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function LN(e){let t=Hc(await B(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await I(e,{type:n?"pr.exists.answered":"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.box,payload:{ts:Date.now(),offerId:t.offerId,...n?{choice:"dismiss"}:{action:"dismiss",actions:null},reason:"interrupt"}}),t.offerId}async function UN(e,t){if(t?.agent!=="claude")return null;let n=[...e.events||[]].reverse().find(r=>r.type==="said"&&r.text);if(!n)return null;for(let r of FN){await rr(r);let o=await Ae(e.leadBox,{lines:De}).catch(()=>""),s=Xs(t.agent,o);if(s&&zu(n.text,s))return await W0(e.leadBox,t.agent,n.text.length+8),pe(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function qN(e,t){if(t?.agent!=="claude")return null;let n=e.events||[],r=[...n].reverse().find(s=>s.type==="said"&&s.text);if(!r||r.kind||Date.now()-r.at>HN||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ne(r.text))return null;for(let s of WN){await rr(s);let a;try{a=await Ad(t,{})}catch{return null}if(lg(a,r))return null;let i=await Ae(e.leadBox,{lines:De}).catch(()=>"");if(Xs(t.agent,i))return null}return pe(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function GN(e,t,n){if(t?.agent!=="claude")return!0;for(let r=0;r<=wp;r++){let o=!1;for(let s of KN){await rr(s);let a=await Ae(e.leadBox,{lines:De}).catch(()=>""),i=ug(t.agent,n,a,{busy:Gs(t.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<wp&&(await Oe(e.leadBox,["Enter"],{kind:"submit-retry"}),z({flow:"say",phase:"submit-retry",trackId:e.id,sessionName:e.leadBox,agent:t.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return z({flow:"say",phase:"submit-failed",trackId:e.id,sessionName:e.leadBox,agent:t.agent,retries:wp,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function W0(e,t,n){for(let r=0;r<3;r++){await po(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await rr(200);let o=await Ae(e,{lines:De}).catch(()=>""),s=Xs(t,o);if(!s)return!0;n=s.length+8}return!1}function ns(e,t,{source:n}={}){e.draftRev=(e.draftRev||0)+1;let r=typeof t=="string"?t:"";return r.trim()?e.draft={text:r,updatedAt:Date.now(),...n?{source:n}:{}}:delete e.draft,e.draft||null}function os(e,t){let n=[],r=o=>n.some(s=>Ne(s).includes(Ne(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?ns(e,n.join(`
|
|
162
162
|
|
|
163
|
-
`),{source:"restore"})?.text??null:null}function
|
|
163
|
+
`),{source:"restore"})?.text??null:null}function zN(e,t){let n=Ne(t);if(!n)return!1;if(rs(e.id).some(o=>Ne(o.text)===n))return!0;let r=e.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>VN)break;if(!(s.type!=="said"||!s.text||Ne(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Bp(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=te(await G(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=_o(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&zN(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;Ne(o).includes(Ne(i))?ns(r,o):ns(r,`${i}
|
|
164
164
|
|
|
165
|
-
${o}`,{source:"restore"})}else
|
|
166
|
-
`);return`${
|
|
165
|
+
${o}`,{source:"restore"})}else ns(r,o);return await Z(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Dp(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=te(await G(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await oe(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=hb(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await Ls(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await Dn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:La(o.agent)});let u=await wb(a,r.leadBox,i);return pe(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await Z(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function q0(e){for(let t of e){if(!t||!await Io(t))continue;let n=await Gy(t).catch(()=>null);if(!n)continue;if((await D.stat(F.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function lt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:F.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function Go(e){let t=[];for(let n of lt(e?.workspace)){if(!n?.dir)continue;let r=await ra(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await oa(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function K0(e){let t=e;typeof t=="string"&&!F.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await ro(t)??await ki(t));let n=await q0([t]);if(!n)throw new Error(`tagged repo "${e}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function JN(e,t,n,r){let o=`track/${tm(t)}-${e}`,s=[];try{await D.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await K0(i);if(s.some(m=>m.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await de(c).catch(()=>null),u=F.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let m=2;a.has(u);m++)u=`${F.basename(c)}-${m}`;a.add(u);let f=F.join(n,u),d=await Oo(c);await $o(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await Wa(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await at(i.base,i.dir).catch(()=>{});throw await D.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function jp(e,t){let n=await G(),r=te(n,e);if(!r)throw new Error(`track "${e}" 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 K0(typeof t=="string"?t.trim():t);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await de(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=F.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${F.basename(s)}-${d}`;let l=F.join(o.dir,c),u=await Oo(s);await Vy(s,o.branch)?await ic(s,l,o.branch):await $o(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 Wa(o.dir,o.repos,o.branch),pe(r,"retagged",{action:"add",repo:a??s,name:c,workspace:G0(o)}),await Z(),f}async function Lp(e,t){let n=await G(),r=te(n,e);if(!r)throw new Error(`track "${e}" 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===t||c.repo===t||c.base===t));if(!s)throw new Error(`repo "${t}" 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 ct.values())if(c.trackId===e&&!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 ra(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await oa(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 at(s.base,s.dir).catch(()=>{}),await D.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Wa(o.dir,o.repos,o.branch),pe(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:G0(o)}),await Z(),{removed:s.name}}async function YN(e,t,{repo:n,repos:r,cwd:o}={}){await D.mkdir($a,{recursive:!0});let s=F.join($a,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return JN(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!F.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await ro(n)??await ki(n));let i=await q0([n,o,hN]);if(i){let c=`track/${tm(t)}-${e}`;try{let l=await Oo(i);await $o(i,s,c,l),await Pa(s);let u=await de(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await D.mkdir(s,{recursive:!0}),await Ld(s),await Pa(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Pa(e){try{await D.writeFile(F.join(e,"AGENTS.md"),Ra,{flag:"wx"})}catch{return}await eP(e,["/AGENTS.md"])}function QN(e,t){let n=e.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
166
|
+
`);return`${ZN}
|
|
167
167
|
|
|
168
168
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
169
|
-
git worktree per tagged repo, ALL on the same branch \`${
|
|
169
|
+
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
170
170
|
|
|
171
171
|
${n}
|
|
172
172
|
|
|
@@ -175,39 +175,39 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
175
175
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
176
176
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
177
177
|
or preview an app, run it from that app's own repo directory.
|
|
178
|
-
`}async function
|
|
179
|
-
`).includes(s));o.length&&await
|
|
178
|
+
`}async function Wa(e,t,n){let r=QN(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await D.writeFile(F.join(e,o),r).catch(()=>{})}async function z0(e){let{stdout:t}=await O0("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return F.resolve(e,t.trim())}async function eP(e,t){try{let n=await z0(e);await D.mkdir(F.dirname(n),{recursive:!0});let r=await D.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
179
|
+
`).includes(s));o.length&&await D.appendFile(n,(r.endsWith(`
|
|
180
180
|
`)||!r?"":`
|
|
181
181
|
`)+o.join(`
|
|
182
182
|
`)+`
|
|
183
|
-
`)}catch{}}async function
|
|
184
|
-
`).filter(s=>s!==
|
|
185
|
-
`).length&&await
|
|
186
|
-
`))}catch{}}async function
|
|
183
|
+
`)}catch{}}async function tP(e,t){try{let n=await z0(e),r=await D.readFile(n,"utf8"),o=r.split(`
|
|
184
|
+
`).filter(s=>s!==t);o.length!==r.split(`
|
|
185
|
+
`).length&&await D.writeFile(n,o.join(`
|
|
186
|
+
`))}catch{}}async function Fp(){let e={refreshed:0,removed:0,codexGlobal:null};try{await D.mkdir(qt,{recursive:!0}),await D.writeFile(F.join(qt,"CLAUDE.md"),XN).catch(()=>{}),await D.writeFile(F.join(qt,"AGENTS.md"),Ra).catch(()=>{});let t=process.env.CODEX_HOME||F.join(process.env.HOME||"/tmp",".codex");if((await D.stat(t).catch(()=>null))?.isDirectory()){let r=F.join(t,"AGENTS.md"),o=await D.readFile(r,"utf8").catch(()=>null);o===null?(await D.writeFile(r,yp,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):gp(o)&&o!==yp&&(await D.writeFile(r,yp).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[$a,as]){let o=await D.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=F.join(r,s.name),i=F.join(a,"AGENTS.md"),c=await D.readFile(i,"utf8").catch(()=>null);c!==null&&gp(c)&&c!==Ra&&(await D.writeFile(i,Ra).catch(()=>{}),e.refreshed++);let l=F.join(a,"CLAUDE.md"),u=await D.readFile(l,"utf8").catch(()=>null);u!==null&&gp(u)&&(await D.rm(l,{force:!0}).catch(()=>{}),await tP(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function J0(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of lt(t))n.base&&n.dir&&await at(n.base,n.dir).catch(()=>{});await D.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await at(t.base,t.dir):await D.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function ol({name:e,cwd:t,repo:n,repos:r,agent:o="claude",skipPermissions:s=!0}){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");let a=await Fa(o,s),i=await G(),c=is().slice(0,8),l=await YN(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(h=>(h.boxes||[]).map(w=>w.name))),f=`${e.trim()} lead`;u.has(Pi(f))&&(f=`${e.trim().slice(0,50).trim()} ${c} lead`);let d=await Nn(f,l.dir,{agentCmd:a,agent:o});Vs({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a});let m={id:c,name:e.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[Vp(d,"lead","live")],events:[],createdAt:Date.now()};return pe(m,"opened",{agent:o,box:d.name,skipPermissions:s,name:m.name,cwd:m.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:h,base:w,baseRef:y,repo:b})=>({name:h,base:w,baseRef:y,repo:b}))}:{}}}),i.tracks.push(m),await Z(),Kt(m)}async function Up({provider:e="claude",fresh:t=!1}={}){if(!nP.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await G()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Kt(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await Vr(r.id).catch(()=>{})}let o=(await Fa(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=is().slice(0,8),a=F.join(qt,"setup",s);await D.mkdir(a,{recursive:!0});let i=await Nn(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&Vs({cwd:i.cwd,sessionName:i.name,agent:e,agentCmd:o});let c={id:s,name:`Connect ${e}`,kind:"setup",setup:{provider:e,startedAt:Date.now(),connectedAt:null},cwd:i.cwd,workspace:{dir:a,base:null,branch:null,kind:"scratch"},leadBox:i.name,boxes:[Vp(i,"lead","live")],events:[],createdAt:Date.now()};return pe(c,"opened",{agent:e,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await G()).tracks.push(c),await Z(),Kt(c)}async function Hp(e,t,n){await wx(e).catch(()=>{});let r=ss.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),ss.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of zr(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await om(a).catch(()=>{}),ct.delete(a.id);for(let a of Object.values(t.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await G(),s=new Set((o.tracks||[]).filter(a=>a.id!==e&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of t.boxes)s.has(a.name)||await Ct(a.name).catch(()=>{})}async function Wp(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await G(),o=r.tracks.findIndex(a=>a.id===e);if(o===-1)throw new Error(`track "${e}" not found`);let s=r.tracks[o];return t||await Bx(s),Pp(e),await Hp(e,s,"track deleted"),n?await p0(e).catch(()=>{}):await f0(e).catch(()=>{}),await bb(),await J0(s),t&&await Dx(e).catch(()=>{}),t?await e0(e).catch(()=>{}):await Qx(s).catch(()=>{}),pe(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await Z(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Vr(e){let t=await G(),n=te(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is already archived`);return n.archivedAt=Date.now(),Pp(e),pe(n,"archived",{boxes:n.boxes.length}),await Z(),n.kind==="setup"?(await Y0(e),Kt(n)):(Ba.add(e),sP(e).catch(()=>{}).finally(()=>Ba.delete(e)),{...await Kt(n),archiving:!0})}async function sP(e){try{await aP(e)}catch{}await Y0(e).catch(()=>{})}async function Y0(e){let t=await G(),n=te(t,e);if(!(!n||!n.archivedAt)){await Hp(e,n,"track archived");for(let r of n.boxes)nr.has(r.state)||(r.state="stopped");await Z()}}async function aP(e){let t=await G(),n=te(t,e);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await oe(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Kg(n.leadBox,i.cwd,{timeoutMs:oP})),s="lead-cli"}catch{}if(r||(r=sy(n,await B(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await I(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await Z()}async function sn(e,t){let n=await Je(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await X0(n);if(r.length){for(let o of r)await Br(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Vr(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function X0(e){return(await Dr().catch(()=>[])).filter(n=>n.trackId===e.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===e.repo&&n.number===e.number))}async function an(e,t){return sl(e,t,"merged")}async function sl(e,t,n="merged"){let r=await Je(e,t).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 X0(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await qb(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await yt(r.trackId,Z0(a),{kind:"merge-notify"}),!0}catch{return!1}}function Z0(e){let t=i=>`${i.repo?`${i.repo}`:""}#${i.number}${i.title?` "${i.title}"`:""}${i.url?` (${i.url})`:""}`;if(e.length===1){let i=e[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 ${I0[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=e.filter(i=>i.outcome==="merged"),r=e.filter(i=>i.outcome!=="merged"),o=r.length?`${n.length} of the ${e.length} PRs on this Track merged; ${r.length} did not land:`:`All ${e.length} PRs on this Track have merged:`,s=e.map(i=>`- ${t(i)} \u2014 ${I0[i.outcome]??i.outcome}`).join(`
|
|
187
187
|
`),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}
|
|
188
188
|
${s}
|
|
189
|
-
${a}`}async function Zc(t){let e=t.boxes.find(o=>o.name===t.leadBox);if(!e)return!1;let n=mp.get(e.name);if(n)return n;let r=WN(t,e).finally(()=>mp.delete(e.name));return mp.set(e.name,r),r}async function WN(t,e){if(t.archivedAt||Jn.has(e.state)||await nt(e.name))return!1;let n=xp(e.agent,e.sessionId),r=await Ei(n);if(r===ro)return pt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===oo&&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 In(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=ws(o.hookToken),Fs({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 jp(t){let e=await G(),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,Ia.delete(t),pt(n,"unarchived",{}),await Zc(n),await Y(),Ue(n)}async function Lp(){let t=await G(),e=new Set((await be()).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),!Jn.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Y(),r}async function Qc(t){let e=await G(),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(Jn.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await nt(n.leadBox)){let a=await xt(n.leadBox,{lines:Nt}).catch(()=>"");if(Bi(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 Zc(n);if(await Y(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Ue(n)}async function Fp(t,e,n){let r=await X(t);if(!r)throw new Error(`track "${t}" not found`);if(!e)throw new Error("unknown offer");let o=await B(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 Qc(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 tl(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 Wc(t,e){let n=await G(),r=Z(n,t);r&&(r.handoff={...r.handoff||{},...e},await Y())}function U0(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 Hp(){let t=await G(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=U0(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await nt(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=tl(l),f=d[d.length-1],p=f.agent,h=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:Ur,at:u},pt(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:h,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 z({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 z({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 el(t,e,n=!0,r=null){let o=Xo.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Z(await G(),t);if(!s)throw new Error(`track "${t}" not found`);Na(t,"handoff");let a=await Ca(e,n),i=Ir(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 nt(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:T0(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Xo.set(t,l),await Wc(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),qN(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 Wc(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),z({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),z({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function qN(t,e,n,r,o){let s=async(R,V={})=>{t.step=R,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${R}`),await Wc(t.trackId,{step:R,toBox:t.toBox}),await z({flow:"lead-handoff",phase:R,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:V.sessionName,...V})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await G(),i=Z(a,t.trackId)?.boxes.find(R=>R.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:t.fromBox}),t.checkpoint="llm",Ir(c))t.checkpoint="skipped-capped";else try{await nd(t.fromBox,{timeoutMs:v0})}catch(R){t.checkpoint="reconstructed",await z({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:t.trackId,fromBox:t.fromBox,error:R?.message||String(R)}).catch(()=>{})}t.checkpoint!=="llm"&&(t.statusArtifact=await rd(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 ld(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await On({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:Oa(o)});let p=await xt(t.fromBox,{lines:Nt}).catch(()=>"");if(!Di(o,p)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let R=xp(c,l,{skipPermissions:t.skipPermissions});await ld(t.fromBox,{agentCmd:R,agent:c,resumeSessionId:l,pinned:u});let V=await G(),M=Z(V,t.trackId);if(M){let N=M.boxes.find(T=>T.name===t.fromBox);N&&(N.state="live"),pt(M,"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 h=await G(),w=Z(h,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(),x=tl(y),A=x[x.length-1];A.endedAt=k,t.reason&&(A.endReason=t.reason),x.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||Ur,at:k},pt(w,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:x.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 Wc(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}async function qp(t){let e=kp.get(t);if(e&&!e.finishedAt)throw new Error("a transfer is already running for this Track");let n=await G(),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=Xo.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of ae.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 kp.set(t,s),KN(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 KN(t,e){let n=e.boxes.find(u=>u.name===e.leadBox)||null,r=n?await nt(n.name).catch(()=>null):null;if(t.checkpoint="llm",!r||n&&Ir(n.agent))t.checkpoint=r?"skipped-capped":"skipped-dead";else try{await nd(e.leadBox,{timeoutMs:v0})}catch{t.checkpoint="reconstructed"}t.checkpoint!=="llm"&&await rd(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 ie(e.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await mn(u.dir,["push","origin",o],bp);let{stdout:f}=await mn(u.dir,["rev-parse","HEAD"]);d.baseSha=f.trim(),d.pushed=!0}catch(f){d.error=String(f?.message||f).split(`
|
|
190
|
-
`)[0]}s.push(
|
|
191
|
-
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await
|
|
189
|
+
${a}`}async function al(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)return!1;let n=bp.get(t.name);if(n)return n;let r=iP(e,t).finally(()=>bp.delete(t.name));return bp.set(t.name,r),r}async function iP(e,t){if(e.archivedAt||e.transferredTo||nr.has(t.state)||await oe(t.name))return!1;let n=Ep(t.agent,t.sessionId),r=await Ni(n);if(r===co)return pe(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;r===lo&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await Nn(t.name,t.cwd,{agentCmd:n,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ss(o.hookToken),Vs({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:n}),t.state="live",pe(e,"rehydrated",{box:t.name,agent:t.agent}),!0}catch(o){return pe(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(o?.message||o)}),!1}}async function qp(e){let t=await G(),n=te(t,e);if(!n)throw new Error(`track "${e}" not found`);if(!n.archivedAt)throw new Error(`track "${e}" is not archived`);return delete n.archivedAt,Ba.delete(e),pe(n,"unarchived",{}),await al(n),await Z(),Kt(n)}async function Kp(){let e=await G(),t=new Set((await xt()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(s.archivedAt)for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Ct(a.name).catch(()=>{}),pe(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!nr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Z(),r}async function il(e){let t=await G(),n=te(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);Xp(n);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${e}" has no lead box`);if(nr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await oe(n.leadBox)){let a=await Ae(n.leadBox,{lines:De}).catch(()=>"");if(qi(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ct(n.leadBox),r.state="stopped"}let s=await al(n);if(await Z(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Kt(n)}async function Gp(e,t,n){let r=await ee(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await B(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await il(e),await I(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:t,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function cl(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Xc(e,t){let n=await G(),r=te(n,e);r&&(r.handoff={...r.handoff||{},...t},await Z())}function Q0(e,t=Date.now()){let n=e?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=e.boxes?.find?.(i=>i.name===e.leadBox)||null;return(o?e.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&e.leadBox===n.toBox)?{...n,step:"done",finishedAt:t,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${r}"; lead "${e.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function zp(){let e=await G(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=Q0(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await oe(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=cl(l),d=f[f.length-1],m=d.agent,h=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Gr,at:u},pe(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:m,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await z({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 z({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ct(i).catch(()=>{}),a&&await Ct(a).catch(()=>{})}return t&&await Z(),t}async function ll(e,t,n=!0,r=null){let o=ss.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=te(await G(),e);if(!s)throw new Error(`track "${e}" not found`);Ha(e,"handoff");let a=await Fa(t,n),i=Pr(t);if(i){let u=new Error(`can't hand off to ${t} \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 oe(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:B0(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ss.set(e,l),await Xc(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),cP(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await Xc(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),z({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),z({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function cP(e,t,n,r,o){let s=async(_,q={})=>{e.step=_,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${_}`),await Xc(e.trackId,{step:_,toBox:e.toBox}),await z({flow:"lead-handoff",phase:_,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:q.sessionName,...q})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await G(),i=te(a,e.trackId)?.boxes.find(_=>_.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",Pr(c))e.checkpoint="skipped-capped";else try{await ld(e.fromBox,{timeoutMs:N0})}catch(_){e.checkpoint="reconstructed",await z({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:_?.message||String(_)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await ud(e.trackId,n.cwd,{trackName:t.name||t.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:e.fromBox,checkpoint:e.checkpoint,statusArtifact:e.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await wd(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox}),await Dn({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:La(o)});let m=await Ae(e.fromBox,{lines:De}).catch(()=>"");if(!Ki(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let _=Ep(c,l,{skipPermissions:e.skipPermissions});await wd(e.fromBox,{agentCmd:_,agent:c,resumeSessionId:l,pinned:u});let q=await G(),C=te(q,e.trackId);if(C){let N=C.boxes.find(T=>T.name===e.fromBox);N&&(N.state="live"),pe(C,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await Z()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let h=await G(),w=te(h,e.trackId);if(!w)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=w.boxes.find(_=>_.name===e.fromBox);if(!y)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let b=Date.now(),x=cl(y),E=x[x.length-1];E.endedAt=b,e.reason&&(E.endReason=e.reason),x.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:b,endedAt:null}),y.agent=o,y.sessionId=d.sessionId,y.pinned=d.pinned,y.state="live",y.role="lead",w.pendingHandoffHydrate={artifact:e.statusArtifact||Gr,at:b},pe(w,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:x.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await Z(),await s("done",{sessionName:e.fromBox}),await Xc(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Yp(e){let t=Ap.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await G(),r=te(n,e);if(!r)throw new Error(`track "${e}" 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=ss.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of ct.values())if(a.trackId===e&&!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:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return Ap.set(e,s),lP(s,r).catch(a=>{s.error=a?.message||String(a),s.finishedAt=Date.now(),console.error(`[transfer-out ${e}] FAILED at step "${s.step}": ${s.error}`)}),s}async function lP(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await oe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Pr(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await ld(t.leadBox,{timeoutMs:N0})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await ud(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of lt(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await yn(u.dir,["push","origin",o],Tp);let{stdout:d}=await yn(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
190
|
+
`)[0]}s.push(f)}e.step="wip";let a=await cp({id:t.id,cwd:t.cwd}),i=await G(),c=te(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&cl(l),await Z(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.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:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function Xp(e){if(!e?.transferredTo)return;let t=new Error(`track "${e.id}" moved to node ${e.transferredTo} \u2014 drive it there`);throw t.transferredTo=e.transferredTo,t}async function ul(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await G(),o=te(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};Pp(e),await Hp(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();for(let i of o.boxes){nr.has(i.state)||(i.state="stopped");let c=Array.isArray(i.epochs)?i.epochs[i.epochs.length-1]:null;c&&typeof c=="object"&&c.endedAt==null&&(c.endedAt=s,c.endReason==null&&(c.endReason=n==="recover"?"node-offline":"node-transfer"))}let a=await lp({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,pe(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await Z(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function Zp(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await ul(n.id,{toNode:n.toNode,mode:n.mode==="recover"?"recover":"transfer"});t.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 t}async function R0(e,t,n){try{await ic(e,t,n)}catch{await yn(e,["worktree","prune"]).catch(()=>{}),await ic(e,t,n)}return t}async function Qp(e={}){let{trackId:t,name:n,agent:r="claude",skipPermissions:o=!0,mode:s="transfer"}=e;if(!t||typeof t!="string"||!/^[0-9a-f]{8}$/.test(t))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(kp.has(t))throw new Error("an adopt is already running for this Track");kp.set(t,{startedAt:Date.now()});try{return await uP({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{kp.delete(t)}}async function uP({trackId:e,name:t,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:m=null,reason:h=null}){let w=await Fa(n,r),y=await G(),b=te(y,e);if(b&&!b.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);b&&(await lp({id:b.id,cwd:b.cwd}).catch(()=>{}),await J0(b),y.tracks.splice(y.tracks.indexOf(b),1),await Z()),await D.mkdir($a,{recursive:!0});let x=F.join($a,e);await D.rm(x,{recursive:!0,force:!0}).catch(()=>{});let E=null,_=!1,q=[];try{let C=[];if(i)for(let g of Array.isArray(c)?c:[]){let p=typeof g=="string"?g:g?.repo;if(!p||!/^[^/\s]+\/[^/\s]+$/.test(p))continue;let S=await ro(p)??await ki(p);await yn(S,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Tp),C.push({slug:p,base:S,name:typeof g=="object"?g?.name??null:null})}if(i&&C.length===1){let{base:g,slug:p}=C[0];await R0(g,x,i),q.push({base:g,dir:x}),await Pa(x);let S=await Oo(g);E={dir:x,base:g,branch:i,kind:"worktree",baseRef:S,repo:p}}else if(i&&C.length>=2){await D.mkdir(x,{recursive:!0});let g=new Set([".bostrat"]),p=[];for(let S of C){let $=S.name||F.basename(S.base);g.has($)&&($=S.slug.replace("/","-"));for(let Se=2;g.has($);Se++)$=`${F.basename(S.base)}-${Se}`;g.add($);let we=F.join(x,$);await R0(S.base,we,i),q.push({base:S.base,dir:we});let ge=await Oo(S.base);p.push({name:$,dir:we,base:S.base,baseRef:ge,repo:S.slug})}await Wa(x,p,i),E={dir:x,kind:"multi",branch:i,repos:p}}else _=!0,await D.mkdir(x,{recursive:!0}),await Ld(x),await Pa(x),E={dir:x,base:null,branch:null,kind:"repo"};let N=!1,T=null;if(l?.sha&&E.kind==="worktree")try{await yn(x,["fetch","origin",l.ref||ip(e)],Tp);let{stdout:g}=await yn(x,["rev-parse","FETCH_HEAD^"]),{stdout:p}=await yn(x,["rev-parse","HEAD"]);g.trim()===p.trim()?(await yn(x,["read-tree","--reset","-u","FETCH_HEAD"]),await yn(x,["reset","-q"]),N=!0):T="snapshot does not parent the fetched branch head"}catch(g){T=String(g?.message||g).split(`
|
|
191
|
+
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await T0({id:e,cwd:x}).catch(()=>({restored:!1}));let W=F.join(x,Gr),H=null;(await Ii(x)).restored?H="checkpoint":typeof u=="string"&&u.trim()&&(await D.mkdir(F.dirname(W),{recursive:!0}),await D.writeFile(W,u),H="brief");let J=new Set(y.tracks.flatMap(g=>(g.boxes||[]).map(p=>p.name))),A=null;m&&!J.has(m)&&!await oe(m).catch(()=>null)?A=m:(A=`${t.trim()} lead`,J.has(Pi(A))&&(A=`${t.trim().slice(0,50).trim()} ${e} lead`));let j=await Nn(A,x,{agentCmd:w,agent:n});Vs({cwd:j.cwd,sessionName:j.name,agent:n,agentCmd:w});let Q=Date.now(),fe=Vp(j,"lead","live"),Re=(Array.isArray(f)?f:[]).filter(g=>g&&typeof g=="object").map(g=>({...g})),ue=Re[Re.length-1];ue&&ue.endedAt==null&&(ue.endedAt=Q,ue.endReason==null&&(ue.endReason=o==="recover"?"node-offline":"node-transfer")),Re.push({agent:n,sessionId:j.sessionId??null,pinned:j.pinned===!0,startedAt:Q,endedAt:null}),fe.epochs=Re;let ie={id:e,name:t.trim(),cwd:j.cwd,workspace:E,leadBox:j.name,boxes:[fe],events:[],createdAt:Number.isFinite(d)?d:Q,...H?{pendingHandoffHydrate:{artifact:Gr,at:Q}}:{}};return pe(ie,"transferred-in",{box:j.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,branch:E.branch??null,wip:N,epoch:Re.length-1,name:ie.name,cwd:ie.cwd,workspace:{base:E.base??null,branch:E.branch??null,kind:E.kind,...E.baseRef?{baseRef:E.baseRef}:{},...E.repo?{repo:E.repo}:{},...E.repos?{repos:E.repos.map(({name:g,base:p,baseRef:S,repo:$})=>({name:g,base:p,baseRef:S,repo:$}))}:{}}}),y.tracks.push(ie),await Z(),{...await Kt(ie),adopt:{wipApplied:N,wipSkipped:T,codeless:_,artifactSource:H}}}catch(C){for(let N of q)await at(N.base,N.dir).catch(()=>{});throw await D.rm(x,{recursive:!0,force:!0}).catch(()=>{}),C}}async function ev(e,t,n){let r=await G(),o=te(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await Z())}async function es(e,t){e.step=t,await qa(e)}function tv(e,t=Date.now()){if(!e||e.finishedAt)return null;let n=e.step;return["snapshot","spawn","working"].includes(n)?{...e,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...e,step:"error",finishedAt:t,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 ${e.branch}`,recovered:"partial-merge"}}function nv(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function mP(e,t,n){if(!nv(n)||ct.has(t)||!await oe(n.box).catch(()=>null))return!1;try{await D.access(n.dir)}catch{return!1}let o=await oe(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return ct.set(t,s),ov(s,o).catch(a=>rv(s,a)),!0}async function em(){let e=await G(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(ct.has(r))continue;if(await mP(n,r,o)){t=!0,pe(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await z({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=tv(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!nr.has(i.state)&&(i.state="canceled"),pe(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 z({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await $0(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||nr.has(r.state)||ct.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?F.join(as,`${n.id}-${r.subtaskId}`):null;await z({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await $0(n,r.name,o)}}return t=await hP(e)||t,t&&await Z(),t}async function $0(e,t,n){if(t&&await Ct(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of lt(e.workspace)){let s=F.join(n,o.name);await at(o.base,s).then(()=>!0,()=>!1)||await at(o.dir,s).catch(()=>{})}await D.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await oe(e.leadBox).catch(()=>null);r?.cwd&&await at(r.cwd,n).catch(()=>{})}async function hP(e){let t;try{t=await D.readdir(as)}catch{return!1}let n=new Set;for(let o of e.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 ct.values())o.dir&&n.add(F.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=F.join(as,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of lt(a.workspace))await at(i.base,F.join(s,i.name)).catch(()=>{});await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await oe(a.leadBox).catch(()=>null):null;i?.cwd?await at(i.cwd,s).catch(()=>{}):await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await z({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function nm(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!_o(n))throw new Error("task (string) required");let s=await Fa(t,r),a=Pr(t);if(a){let m=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw m.usageCap=a,m}let i=te(await G(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await oe(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 m=lt(i.workspace);if(!m.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let h of m)if(!h?.dir||!await Io(h.dir))throw new Error(`tagged checkout "${h?.name??"?"}" is missing from the lead workspace (${h?.dir??"no dir"})`);u=m.map(h=>({name:h.name,leadDir:h.dir,base:null}))}else if(!await Io(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=is().slice(0,8),d={id:f,trackId:e,trackName:i.name,agent:t,task:_o(n),skipPermissions:r,reason:B0(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${tm(i.name)}-${f}`,base:null,repos:u,dir:F.join(as,`${e}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return ct.set(f,d),await qa(d),wP(d,c,s).catch(m=>rv(d,m)),d}async function rv(e,t){e.failedStep=e.step,e.error=t instanceof Da?"canceled":t?.message||String(t),e.step=t instanceof Da?"canceled":"error",e.finishedAt=Date.now(),await om(e).catch(()=>{}),await qa(e).catch(()=>{}),await kP(e).catch(()=>{})}function fl(e){let t=ct.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");return t.canceled=!0,t}function rm(e){let t=ct.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");if(t.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${t.step})`);return t.forceMerge=!0,t}async function wP(e,t,n){if(await es(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Ro(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await qa(e)}else e.base=await Ro(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await es(e,"spawn"),await D.mkdir(as,{recursive:!0}),e.repos){await D.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await $o(a.leadDir,F.join(e.dir,a.name),e.branch,a.base);let s=lt((te(await G(),e.trackId)||{}).workspace);await Wa(e.dir,e.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),e.branch)}else await $o(t.cwd,e.dir,e.branch,e.base),await Pa(e.dir);await gP(e,t);let r=e.trackName.slice(0,38).trim(),o=await Nn(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await yP(e,o),await D0(e.trackId,o.name,gN(e.task,!!e.repos),{kind:"subtask"}),await ov(e,t)}async function ov(e,t){await es(e,"working");let n=F.join(e.dir,Yc);if(await vP(n,e),e.canceled)throw new Da;if(await es(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=F.join(e.dir,l.name);await jd(u,`bostrat: subtask ${e.id} result (auto-committed)`),l.stat=await Ud(l.leadDir,l.base,e.branch,Qo),l.files=await Hd(l.leadDir,l.base,e.branch,Qo);let f=await Wd(l.leadDir,l.base,e.branch,Qo);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=>F.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:F.join(l.name,d))),f.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await jd(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`),e.stat=await Ud(t.cwd,e.base,e.branch,Qo),e.files=await Hd(t.cwd,e.base,e.branch,Qo);let s=await Wd(t.cwd,e.base,e.branch,Qo);e.applied=s.applied,e.conflicts=s.conflicts}await qa(e);let r=await xP(e,t,n);await es(e,"resume"),await D0(e.trackId,e.leadBox,yN(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await es(e,"reap"),await om(e),e.step="done";let o=Date.now();await ev(e.trackId,e.id,{...dl(e),finishedAt:o}),await bP(e,o),e.finishedAt=o}async function gP(e,t){let n=F.join(e.dir,F.dirname(Oa));await D.mkdir(n,{recursive:!0});let r="";try{r=await Pw(t.cwd,t.sessionId)}catch{}if(!r)try{r=await D.readFile(F.join(t.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 ${e.id}
|
|
192
192
|
|
|
193
|
-
**Delegated task:** ${
|
|
193
|
+
**Delegated task:** ${e.task}
|
|
194
194
|
|
|
195
|
-
**Lead agent:** ${
|
|
195
|
+
**Lead agent:** ${t.agent||"claude"} \xB7 **workspace:** ${t.cwd}
|
|
196
196
|
|
|
197
197
|
---
|
|
198
198
|
|
|
199
199
|
## Conversation so far (lead \u2192 you)
|
|
200
200
|
|
|
201
201
|
${r}
|
|
202
|
-
`;await
|
|
202
|
+
`;await D.writeFile(F.join(e.dir,Oa),o)}async function yP(e,t){let n=await G(),r=te(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:Ss(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),pe(r,"subtask-delegated",{to:t.name,toAgent:e.agent,task:e.task,subtaskId:e.id,skipPermissions:e.skipPermissions,reason:e.reason}),r.subtasks=r.subtasks||{},r.subtasks[e.id]={...r.subtasks[e.id]||{},...dl(e)},await Z())}async function bP(e,t=e.finishedAt){let n=await G(),r=te(n,e.trackId);if(!r)return;let o=r.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=e.conflicts.length?"conflict":"merged",o.stat=e.stat),pe(r,"subtask-merged",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,stat:e.stat,conflicts:e.conflicts.length,files:e.files,durationMs:t?t-e.startedAt:null}),await Z()}async function kP(e){let t=await G(),n=te(t,e.trackId);if(!n)return;let r=e.canceled||e.error==="canceled",o=n.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=r?"canceled":"failed"),pe(n,r?"subtask-canceled":"subtask-failed",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,failedStep:e.failedStep||e.step,error:e.error,durationMs:e.finishedAt?e.finishedAt-e.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[e.id]={...n.subtasks[e.id]||{},...dl(e)},await Z()}async function xP(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=F.join(t.cwd,r);await D.mkdir(F.dirname(o),{recursive:!0}),await D.copyFile(n,o)}catch{}return r}async function om(e){if(e.box&&await Ct(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await at(t.leadDir,F.join(e.dir,t.name)).catch(()=>{});await D.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await oe(e.leadBox).catch(()=>null))?.cwd;n&&await at(n,e.dir).catch(()=>{})}}async function vP(e,t){let n=(t.startedAt||Date.now())+dP;for(;;){if(t.canceled||t.forceMerge)return;try{await D.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await rr(fP)}}function rr(e){return new Promise(t=>setTimeout(t,e))}var O0,qt,C0,mN,$a,hN,Zc,ja,tr,Gr,N0,wN,Yc,Oa,gN,yN,gt,fp,te,pp,pe,mp,hp,kN,nr,E0,SN,TN,AN,EN,RN,Ca,_0,CN,MN,NN,PN,BN,ts,Ma,FN,HN,WN,KN,wp,VN,G0,Na,V0,XN,Ra,gp,ZN,yp,nP,Ba,rP,oP,I0,bp,Vp,ss,cs,Tp,yn,Ap,Jp,kp,dP,fP,as,Qo,ct,pP,zr,dl,qa,tm,Da,Ur=O(()=>{ze();fd();ay();Gi();Rn();ju();uy();Ge();Yt();As();Ui();_y();Sr();Md();Pd();qy();Ju();Hi();sd();vt();cc();hr();zs();fc();gb();Yd();Vk();id();ed();Jk();_f();le();Uc();ha();Bo();Wf();Yo();Qf();zc();ap();Vn();b0();up();A0();Ri();O0=pN(fN),qt=process.env.BOSTRAT_STATE_DIR||F.join(process.env.HOME||"/tmp",".bostrat"),C0=F.join(qt,"tracks.json"),mN=F.join(qt,"events.jsonl"),$a=F.join(qt,"workspaces"),hN=process.env.BOSTRAT_DEFAULT_REPO||null,Zc={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"}},ja=Object.keys(Zc);tr={at:0,value:null};Gr=".bostrat/handoff/latest.md",N0=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;wN=/\(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\.\)$/;Yc=".bostrat/handoff/subtask-result.md",Oa=".bostrat/handoff/lead-context.md",gN=(e,t=!1)=>t?`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 ${Oa} \u2014 read that first for context. Your scoped task:
|
|
203
203
|
|
|
204
|
-
${
|
|
204
|
+
${e}
|
|
205
205
|
|
|
206
|
-
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 ${
|
|
206
|
+
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 ${Yc} (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 ${Oa} \u2014 read that first for context. Your scoped task:
|
|
207
207
|
|
|
208
|
-
${
|
|
208
|
+
${e}
|
|
209
209
|
|
|
210
|
-
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 ${
|
|
210
|
+
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 ${Yc} 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.`,yN=(e,t,n,r)=>`A subtask box you delegated has finished: "${e}". Its changes are now merged into your working tree (${t.files} file${t.files===1?"":"s"}, +${t.insertions} \u2212${t.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."),gt=null,fp=null;te=(e,t)=>e.tracks.find(n=>n.id===t)||null,pp=Promise.resolve();pe=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),bN(e.id,r),kb(e,r),r};Lw(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await G()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(pe(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await Z().catch(()=>{}))});mp=F.join(qt,"plane-link","update-notice.json");hp=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),kN=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);nr=new Set(["merged","conflict","canceled","failed","handed-off"]),E0=new Map;SN=5e3;TN={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};AN=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];EN=10*6e4;RN=1500,Ca=new Map;_0="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.";CN=4e3,MN=3,NN=[300,350,400],PN=1e3,BN=12,ts=new Map,Ma=new Map;FN=[250,400,500,600];HN=3e4,WN=[350,650,900];KN=[250,450,700],wp=2;VN=12e4;G0=e=>({branch:e.branch,kind:e.kind,repos:(e.repos??[]).filter(Boolean).map(({name:t,base:n,baseRef:r,repo:o})=>({name:t,base:n,baseRef:r,repo:o}))});Na="# bostrat workspace",V0=`${Na}
|
|
211
211
|
|
|
212
212
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
213
213
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -475,68 +475,68 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
475
475
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
476
476
|
implementation turn this way, every time. When the operator has already asked you
|
|
477
477
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
478
|
-
`,
|
|
479
|
-
`)),
|
|
480
|
-
`,`${
|
|
478
|
+
`,XN=V0,Ra=V0+vg;gp=e=>typeof e=="string"&&(e===Na||e.startsWith(`${Na}
|
|
479
|
+
`)),ZN="# bostrat multi-repo workspace";yp=Ra.replace(`${Na}
|
|
480
|
+
`,`${Na}
|
|
481
481
|
|
|
482
482
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
483
483
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
484
484
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
485
485
|
When working anywhere else, ignore everything below.
|
|
486
|
-
`);jN=new Set(["claude","codex","cursor"]);Ia=new Set,LN=t=>Ia.has(t);FN=9e4;w0={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};mp=new Map;Up=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:ws(t.hookToken),createdAt:Date.now()});Xo=new Map,ts=t=>Xo.get(t)||(he?Z(he,t)?.handoff:null)||null;bp=6e4,mn=(t,e,n=15e3)=>b0("git",["-C",t,...e],{timeout:n,maxBuffer:8*1024*1024}),kp=new Map,Wp=t=>kp.get(t)||null;hp=new Map;VN=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,zN=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Zo=q.join(hn,"worktrees"),Ko=[Sa,Hc],ae=new Map,JN=t=>ae.get(t)||(he?he.tracks.map(e=>e.subtasks?.[t]).find(Boolean):null)||null,Wr=t=>{let e=new Map([...ae.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=he?Z(he,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)},rl=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});Ba=t=>H0(t.trackId,t.id,rl(t));zp=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Ra=class extends Error{}});Gt();import dt from"node:path";import Ht from"node:fs";import qj from"node:readline";import{fileURLToPath as Kj}from"node:url";import ge from"express";import Gj from"node:http";import Vj from"node:https";import nh from"node:os";import{WebSocketServer as zj}from"ws";Gt();import RE from"node-pty";async function sw(t,e,n={}){if(!St(e)){Oi(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await _n()){Oi(t,`host tmux server not running on ${re}`),t.close(1011,"tmux server unavailable");return}if(!await nt(e)){Oi(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=ow(n.cols,120,aw),o=ow(n.rows,32,iw),s;try{s=RE.spawn(L,["-S",re,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Oi(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&mu(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(mu(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"&&ME(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{}CE(e)})}var $E=6,OE=250;async function CE(t,e=$E,n=OE){for(let r=0;r<e;r++){if(await pr(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var aw=20,iw=6;function ME(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=aw&&Number(e)>=iw}function ow(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function mu(t,e){try{t.send(JSON.stringify(e))}catch{}}function Oi(t,e){mu(t,{type:"error",message:e})}function cw(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}}ke();Mi();sd();Nr();var sP=t=>!!t&&!t.finishedAt&&!t.canceled&&!t.error;function z0({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(sP)&&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}}st();qt();var aP=(...t)=>fetch(...t);function iP(t=process.env){return!!(gt(t)&&t.BOSTRAT_PLANE_TOKEN)}async function Zp(t=process.env){if(!iP(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 aP(`${gt(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 es(t,e=process.env){let n=String(t??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Zp(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 sl(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}})}st();qt();var cP=(...t)=>fetch(...t);async function al(t,e,n,r=process.env){if(!(gt(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 cP(`${gt(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 lP(t,e=process.env){return al("POST","/v1/circuits",{name:t},e).then(n=>n.circuit)}async function J0({circuitRef:t,newCircuitName:e,name:n,markdown:r},o=process.env){let s=e?await lP(e,o):await es(t,o),a=await al("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Qp(t,e=process.env){let n=await es(t,e),r=await al("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,e);return{circuit:n,...r}}async function tm(t,e=process.env){return(await al("GET","/v1/circuits",null,e)).memberships?.[t]??null}async function Y0(t,e,n,r=process.env){let{blueprint:o,items:s}=await Qp(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}}st();qt();var uP=(...t)=>fetch(...t);async function dP(t,e,n,r=process.env){if(!(gt(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives 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 uP(`${gt(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)}}async function X0(t,{title:e,context:n="",decision:r,alternatives:o=[],supersedesId:s=null}){if(!String(e??"").trim())throw new Error("a title is required");if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await I(t,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:String(e).trim(),context:String(n??"").trim(),decision:String(r).trim(),alternatives:(Array.isArray(o)?o:[]).map(a=>typeof a=="string"?{label:a}:a).filter(a=>a?.label),...s?{supersedesId:s}:{}}}),{title:String(e).trim()}}function Z0(t,e=process.env){return dP("GET",`/v1/decisions?track=${encodeURIComponent(t)}`,null,e)}np();st();import Ha from"node:fs/promises";import hl from"node:path";import eB from"node:os";Sn();import fP from"node:fs/promises";import Q0 from"node:path";import pP from"node:os";var tv=()=>Q0.join(process.env.BOSTRAT_STATE_DIR||Q0.join(process.env.HOME||pP.tmpdir(),".bostrat"),"integrations.json");async function wn(){try{return JSON.parse(await fP.readFile(tv(),"utf8"))||{}}catch{return{}}}var il=["linked","prOpened","prMerged","create"],em=["prompt","auto","off"],mP={create:["prompt","off"]},ev=t=>mP[t]??em;function cl(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of il){let s=ev(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 qr(){let t=await wn(),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:cl(t),defaultTeam:n}:null}async function ll(){let t=await wn(),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:cl(t)}}async function nm(){return cl(await wn())}async function nv(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${il.join(", ")}`);for(let[r,o]of e){if(!il.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${il.join(", ")}`);let s=ev(r);if(!s.includes(o))throw new Error(s.length<em.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${em.join(", ")}`)}let n=await wn();return n.ticketFlow={...cl(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Xt(tv(),n,{mode:384}),n.ticketFlow}async function Da(){let t=(await wn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function hP(){let t=(await wn()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var wP={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function ul(){let e=(await wn()).voiceChat||{},n=e.provider||"openai",r=wP[n];if(!r)return null;let o=n==="openai"?await hP():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function rv(){let t=(await wn()).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 Kr(){let t=(await wn()).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}}st();An();kr();Ho();import La from"node:fs/promises";import fv from"node:path";import UP from"node:os";import{randomUUID as pv}from"node:crypto";var om={};Ul(om,{createComment:()=>AP,findIssueUrl:()=>kP,issueStates:()=>IP,mdToAdf:()=>av,moveIssue:()=>RP,parseIssueRef:()=>bP,resolveIssue:()=>xP,uploadImage:()=>EP,upsertCard:()=>vP,whoami:()=>rm});var ns=t=>String(t.baseUrl||"").replace(/\/$/,""),sv=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Gr(t,e,n,r){let o=await fetch(`${ns(t)}${n}`,{method:e,headers:{authorization:sv(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 gP=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,yP=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function bP(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(gP);return r?r[1].toUpperCase():null}let n=e.match(yP);return n?n[1]:null}function kP(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 xP(t,e){let n;try{n=await Gr(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:`${ns(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function vP(t,e,{url:n,title:r,subtitle:o}){await Gr(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 SP=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function TP(t){let e=[],n=0;for(let r of t.matchAll(SP)){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 av(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
487
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
486
|
+
`);nP=new Set(["claude","codex","cursor"]);Ba=new Set,rP=e=>Ba.has(e);oP=9e4;I0={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};bp=new Map;Vp=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ss(e.hookToken),createdAt:Date.now()});ss=new Map,cs=e=>ss.get(e)||(gt?te(gt,e)?.handoff:null)||null;Tp=6e4,yn=(e,t,n=15e3)=>O0("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Ap=new Map,Jp=e=>Ap.get(e)||null;kp=new Map;dP=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,fP=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,as=F.join(qt,"worktrees"),Qo=[Oa,Yc],ct=new Map,pP=e=>ct.get(e)||(gt?gt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,zr=e=>{let t=new Map([...ct.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=gt?te(gt,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},dl=e=>({id:e.id,trackId:e.trackId,trackName:e.trackName,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,reason:e.reason||null,step:e.step,failedStep:e.failedStep||null,box:e.box,branch:e.branch,base:e.base,repos:e.repos||null,dir:e.dir,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt});qa=e=>ev(e.trackId,e.id,dl(e));tm=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Da=class extends Error{}});ze();import me from"node:path";import qe from"node:fs";import iL from"node:readline";import{fileURLToPath as cL}from"node:url";import bt from"express";import lL from"node:http";import uL from"node:https";import uh from"node:os";import{WebSocketServer as dL}from"ws";ze();import WE from"node-pty";async function pw(e,t,n={}){if(!Ee(t)){Li(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Mn()){Li(e,`host tmux server not running on ${st}`),e.close(1011,"tmux server unavailable");return}if(!await oe(t)){Li(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=fw(n.cols,120,mw),o=fw(n.rows,32,hw),s;try{s=WE.spawn(L,["-S",st,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Li(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&xu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(xu(e,{type:"exit",code:a??null,signal:i??null}),e.close(1e3,"tmux exited"))}),e.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"&&VE(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),e.on("close",()=>{try{s.kill()}catch{}GE(t)})}var qE=6,KE=250;async function GE(e,t=qE,n=KE){for(let r=0;r<t;r++){if(await kr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var mw=20,hw=6;function VE(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=mw&&Number(t)>=hw}function fw(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function xu(e,t){try{e.send(JSON.stringify(t))}catch{}}function Li(e,t){xu(e,{type:"error",message:t})}function ww(e){let t=typeof e=="string"&&e?e:"/",n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.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}}vt();Ui();fd();Ur();var SP=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function av({tracks:e=[],views:t=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of e)!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(SP)&&r.push(`merge-back:${o.id}`));return t.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}le();Ge();var TP=(...e)=>fetch(...e);function AP(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function sm(e=process.env){if(!AP(e))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let t=new AbortController,n=setTimeout(()=>t.abort(),1e4);try{let r=await TP(`${be(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.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 ls(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await sm(t),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 pl(e,t,{source:n="chat"}={}){return I(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}le();Ge();var EP=(...e)=>fetch(...e);async function ml(e,t,n,r=process.env){if(!(be(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 EP(`${be(r)}${t}`,{method:e,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 _P(e,t=process.env){return ml("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function iv({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await _P(t,o):await ls(e,o),a=await ml("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function am(e,t=process.env){let n=await ls(e,t),r=await ml("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function im(e,t=process.env){return(await ml("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function cv(e,t,n,r=process.env){let{blueprint:o,items:s}=await am(t,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(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}le();Ge();var IP=(...e)=>fetch(...e);async function RP(e,t,n,r=process.env){if(!(be(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives 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 IP(`${be(r)}${t}`,{method:e,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)}}async function lv(e,{title:t,context:n="",decision:r,alternatives:o=[],supersedesId:s=null}){if(!String(t??"").trim())throw new Error("a title is required");if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await I(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:String(t).trim(),context:String(n??"").trim(),decision:String(r).trim(),alternatives:(Array.isArray(o)?o:[]).map(a=>typeof a=="string"?{label:a}:a).filter(a=>a?.label),...s?{supersedesId:s}:{}}}),{title:String(t).trim()}}function uv(e,t=process.env){return RP("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}ap();le();import Ya from"node:fs/promises";import Sl from"node:path";import bB from"node:os";Rn();import $P from"node:fs/promises";import dv from"node:path";import OP from"node:os";var fv=()=>dv.join(process.env.BOSTRAT_STATE_DIR||dv.join(process.env.HOME||OP.tmpdir(),".bostrat"),"integrations.json");async function bn(){try{return JSON.parse(await $P.readFile(fv(),"utf8"))||{}}catch{return{}}}var hl=["linked","prOpened","prMerged","create"],cm=["prompt","auto","off"],CP={create:["prompt","off"]},pv=e=>CP[e]??cm;function wl(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of hl){let s=pv(o),a=s.includes(n)?n:"prompt",i=e.ticketFlow?.[o]??t.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function Jr(){let e=await bn(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:wl(e),defaultTeam:n}:null}async function gl(){let e=await bn(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:wl(e)}}async function lm(){return wl(await bn())}async function mv(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${hl.join(", ")}`);for(let[r,o]of t){if(!hl.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${hl.join(", ")}`);let s=pv(r);if(!s.includes(o))throw new Error(s.length<cm.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${cm.join(", ")}`)}let n=await bn();return n.ticketFlow={...wl(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Qe(fv(),n,{mode:384}),n.ticketFlow}async function Ka(){let e=(await bn()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function MP(){let e=(await bn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var NP={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function yl(){let t=(await bn()).voiceChat||{},n=t.provider||"openai",r=NP[n];if(!r)return null;let o=n==="openai"?await MP():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function hv(){let e=(await bn()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function Yr(){let e=(await bn()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}le();On();_r();Yo();import Va from"node:fs/promises";import Tv from"node:path";import sB from"node:os";import{randomUUID as Av}from"node:crypto";var dm={};zl(dm,{createComment:()=>WP,findIssueUrl:()=>jP,issueStates:()=>GP,mdToAdf:()=>yv,moveIssue:()=>VP,parseIssueRef:()=>DP,resolveIssue:()=>LP,uploadImage:()=>qP,upsertCard:()=>FP,whoami:()=>um});var us=e=>String(e.baseUrl||"").replace(/\/$/,""),gv=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Xr(e,t,n,r){let o=await fetch(`${us(e)}${n}`,{method:t,headers:{authorization:gv(e),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 PP=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,BP=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function DP(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(PP);return r?r[1].toUpperCase():null}let n=t.match(BP);return n?n[1]:null}function jP(e,t=null){let n=null;try{n=t?.baseUrl?new URL(t.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(e||"").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 LP(e,t){let n;try{n=await Xr(e,"GET",`/rest/api/3/issue/${encodeURIComponent(t)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${t}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${us(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function FP(e,t,{url:n,title:r,subtitle:o}){await Xr(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var UP=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function HP(e){let t=[],n=0;for(let r of e.matchAll(UP)){r.index>n&&t.push({type:"text",text:e.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);t.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?t.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):t.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<e.length&&t.push({type:"text",text:e.slice(n)}),t}function yv(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
487
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...HP(o))}),{type:"paragraph",content:r.length?r:[{type:"text",text:" "}]}});return{type:"doc",version:1,content:t.length?t:[{type:"paragraph",content:[{type:"text",text:" "}]}]}}async function WP(e,t,n){let r=await Xr(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:yv(n)});return r?.id?`${us(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function um(e){let t=await Xr(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(us(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function qP(e,{issue:t,filename:n,contentType:r,buffer:o}){let s=new FormData;s.append("file",new Blob([o],{type:r}),n);let a=await fetch(`${us(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:gv(e),"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??`${us(e)}/rest/api/3/attachment/content/${c.id}`}var KP={new:"unstarted",indeterminate:"started",done:"completed"},wv=e=>KP[e?.key]??"started";async function GP(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Xr(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Xr(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:wv(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:wv(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function VP(e,t,n){await Xr(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var hm={};zl(hm,{createComment:()=>tB,createIssue:()=>mm,createProject:()=>pm,findIssueUrl:()=>ZP,issueStates:()=>rB,moveIssue:()=>oB,parseIssueRef:()=>XP,resolveIssue:()=>QP,teams:()=>ds,uploadImage:()=>nB,upsertCard:()=>eB,whoami:()=>fm});var zP=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Gt(e,t,n={}){let r=await fetch(zP(),{method:"POST",headers:{"content-type":"application/json",authorization:e.apiKey},body:JSON.stringify({query:t,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 JP=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,YP=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function XP(e){let t=String(e||"").trim(),n=t.match(JP);if(n)return n[1].toUpperCase();let r=t.match(YP);return r?r[1]:null}function ZP(e){let t=String(e||"").match(/https?:\/\/linear\.app\/[^/\s]+\/issue\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/);return t?t[0]:null}async function QP(e,t){let r=(await Gt(e,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:t})).issue;if(!r)throw new Error(`linear: issue "${t}" 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 eB(e,t,{url:n,title:r,subtitle:o}){let s=await Gt(e,"mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }",{input:{issueId:t.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 tB(e,t,n){let r=await Gt(e,"mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } }",{input:{issueId:t.issueId,body:n}});if(!r.commentCreate?.success)throw new Error("linear: commentCreate did not succeed");return r.commentCreate.comment?.url??null}async function fm(e){let t=await Gt(e,"query { viewer { name email } organization { name urlKey } }");return{user:t.viewer?.name??t.viewer?.email??null,org:t.organization?.name??t.organization?.urlKey??null}}async function nB(e,{filename:t,contentType:n,buffer:r}){let o=await Gt(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
488
488
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
489
489
|
success
|
|
490
490
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
491
491
|
}
|
|
492
|
-
}`,{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 LP(t,e){let n=await He(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 FP(t,e,n){if(!(await He(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 rs(t){return((await He(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var iv=255;async function am(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>iv,a=s?`${o.slice(0,iv-1).trimEnd()}\u2026`:o,i=await He(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 im(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await He(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 mv={linear:{api:cm,config:qr},jira:{api:om,config:ll}};async function hm(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(mv)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function Vr(t){let e=mv[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function wm(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 hv=()=>process.env.BOSTRAT_STATE_DIR||fv.join(process.env.HOME||UP.tmpdir(),".bostrat"),fm=()=>fv.join(hv(),"tickets.json"),wv=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),dl=t=>`${wv()}/track/${t}`,Xn=null,lm=null,um=Promise.resolve();async function Ua(){return Xn||(lm??=La.readFile(fm(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(Xn=t,!e&&kt()){let{records:n,source:r}=await Mt("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;Xn[s]=i}Object.keys(Xn).length&&await pm().catch(()=>{})}}return Xn}).finally(()=>lm=null),lm)}async function pm(){let t=JSON.stringify({tickets:Xn},null,2),e=`${fm()}.${process.pid}.tmp`;await La.mkdir(hv(),{recursive:!0}),await La.writeFile(e,t),await La.rename(e,fm())}async function Zn(t=null){if(um=um.then(pm,pm),await um.catch(()=>{}),!t)return;let e=Xn?.[t];e?Kt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):ye("tickets",t).catch(()=>{})}async function We(t){return(await Ua())[t]??null}async function gv(){return{...await Ua()}}async function yv(){return(await hm()).length>0}async function os(t,e,{source:n="api"}={}){let r=await hm();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 Ua())[t.id]=c,await Zn(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 Fa(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 ja(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await xv(t.id,c,"linked"),c}async function gm(t){let e=await Ua();return e[t]?(delete e[t],await Zn(t),!0):!1}var mm=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function bv(t,e){if(!t.track?.cwd)return null;let n=Pn(await B({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function HP(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${mm(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${mm(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function WP(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(mm(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Fa(t,e){let n=await Vr(e);if(!n)return;let r=await bv(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:dl(t),title:`Bostrat Track: ${e.track.name}`,subtitle:WP(e,r)})}function fl(t){let e=new URL(wv());return`${e.protocol}//${t}.${e.host}/`}var qP={linked:"linked",opened:"prOpened",merged:"prMerged"};function KP(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 kv(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Zn(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 xv(t,e,n){let r=await Vr(e);if(!r)return;let o=r.cfg.ticketFlow?.[qP[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=wm(s,a),c=KP(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 kv(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:pv(),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 vv(t){let e=await We(t);if(!e)throw new Error("no ticket linked to this Track");let n=await Vr(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=wm(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await Zn(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 ym(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await We(t);if(!r)throw new Error("no ticket linked to this Track");let o=await Vr(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=wm(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 Zn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await kv(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Sv(t,e){let n=await We(t);if(!n)throw new Error("no ticket linked to this Track");let r=await Vr(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 Tv(t,e,n){if(!e)throw new Error("offerId required");let r=await B({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await ym(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 cv=50;async function Av(){let t=await qr();if(!t)throw new Error("no ticketing integration configured");return rs(t)}function GP(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>cv)throw new Error(`too many issues (${n.length}); cap is ${cv} 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 rs(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 We(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 Ev(t,e){let n=await qr();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=GP(e),o=await rs(n),s=await VP(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=pv();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 _v(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await B({id:t.id,cwd:t.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await 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 qr();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await rs(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let h=await am(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await im(a,{teamId:c.id,title:h.title,description:h.description,projectId:d,priority:h.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:h.title,message:w?.message||String(w)})}await 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(h=>h.kind==="issue"),p=null;return i.linkCurrentTrack&&f.length===1&&!await We(t.id)&&(p=await os(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function ja(t,e){let n=await Vr(t);n&&await n.api.createComment(n.cfg,t,e)}async function zP(t,e,n){let r=await Vr(t);if(!r)return null;let o=await an(e);if(!o)return null;let s=Te(o,n);if(!s)return null;let a=await La.readFile(me(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 lv=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),dm=new Map;function JP(t,e){if(lv()<=0)return Fa(t,e);if(dm.has(t))return;let n=setTimeout(()=>{dm.delete(t),We(t).then(r=>r?Fa(t,r):null).catch(()=>{})},lv());n.unref?.(),dm.set(t,n)}var uv=new Map;async function dv(t,e,n){let r=uv.get(t)??0;Date.now()-r<600*1e3||(uv.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 YP(t,{resolveTrack:e=null}={}){let n=await We(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await hm())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 os(s,o,{source:"chat"})}catch(a){await dv(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 Zn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Zn(t.trackId),await Fa(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 Zn(t.trackId),await Fa(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await bv(n,t.trackId).catch(()=>null):null;await ja(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
492
|
+
}`,{contentType:n,filename:t,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 rB(e,t){let n=await Gt(e,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:t.key});if(!n.issue)throw new Error(`linear: issue "${t.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function oB(e,t,n){if(!(await Gt(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function ds(e){return((await Gt(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var bv=255;async function pm(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>bv,a=s?`${o.slice(0,bv-1).trimEnd()}\u2026`:o,i=await Gt(e,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[t],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 mm(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Gt(e,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:t,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 Ev={linear:{api:hm,config:Jr},jira:{api:dm,config:gl}};async function vm(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(Ev)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Zr(e){let t=Ev[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Sm(e,t){if(!e||e.name)return e;let n=t.find(r=>r.id===e.id);return n?{...e,name:n.name}:e}var _v=()=>process.env.BOSTRAT_STATE_DIR||Tv.join(process.env.HOME||sB.tmpdir(),".bostrat"),bm=()=>Tv.join(_v(),"tickets.json"),Iv=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),bl=e=>`${Iv()}/track/${e}`,or=null,wm=null,gm=Promise.resolve();async function Ja(){return or||(wm??=Va.readFile(bm(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(or=e,!t&&Te()){let{records:n,source:r}=await Be("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;or[s]=i}Object.keys(or).length&&await km().catch(()=>{})}}return or}).finally(()=>wm=null),wm)}async function km(){let e=JSON.stringify({tickets:or},null,2),t=`${bm()}.${process.pid}.tmp`;await Va.mkdir(_v(),{recursive:!0}),await Va.writeFile(t,e),await Va.rename(t,bm())}async function sr(e=null){if(gm=gm.then(km,km),await gm.catch(()=>{}),!e)return;let t=or?.[e];t?Ve("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):kt("tickets",e).catch(()=>{})}async function Vt(e){return(await Ja())[e]??null}async function Rv(){return{...await Ja()}}async function $v(){return(await vm()).length>0}async function fs(e,t,{source:n="api"}={}){let r=await vm();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(t);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.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:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Ja())[e.id]=c,await sr(e.id),await I({id:e.id,cwd:e.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 za(e.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 Mv(e.id,c,"linked"),c}async function Tm(e){let t=await Ja();return t[e]?(delete t[e],await sr(e),!0):!1}var xm=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function Ov(e,t){if(!e.track?.cwd)return null;let n=Un(await B({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function aB(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${xm(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${xm(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function iB(e,t){let n=[];return e.track.leadAgent&&n.push(`lead ${e.track.leadAgent}`),e.track.branch&&n.push(e.track.branch),e.track.repos?.length&&n.push(e.track.repos.join(" + ")),e.pr?.number&&n.push(`PR #${e.pr.number} ${e.pr.change==="merged"?"merged":"open"}`),t&&n.push(xm(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function za(e,t){let n=await Zr(t);if(!n)return;let r=await Ov(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:bl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:iB(t,r)})}function kl(e){let t=new URL(Iv());return`${t.protocol}//${e}.${t.host}/`}var cB={linked:"linked",opened:"prOpened",merged:"prMerged"};function lB(e,t,n){let r=o=>t.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(e?.type))return r("started")[0]??null}else if(n==="opened"){if(e?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&e?.type!=="completed")return r("completed")[0]??null;return null}async function Cv(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await sr(e),await I({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Mv(e,t,n){let r=await Zr(t);if(!r)return;let o=r.cfg.ticketFlow?.[cB[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Sm(s,a),c=lB(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??t.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,t,c.id),await Cv(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await I({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Av(),provider:t.provider,key:t.key,url:t.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...t.pr?.number?{prNumber:t.pr.number}:{}}}).catch(()=>{})}async function Nv(e){let t=await Vt(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Zr(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Sm(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await sr(e)),{key:t.key,url:t.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 Am(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Vt(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Zr(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Sm(s,a),c=a.find(u=>u.id===t);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 sr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Cv(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Pv(e,t){let n=await Vt(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Zr(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await I({id:e,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 Bv(e,t,n){if(!t)throw new Error("offerId required");let r=await B({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===t);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===t))throw new Error("offer already answered");let s=null;return n&&(s=await Am(e.id,o.payload.to?.id,{mode:"offer"})),await I({id:e.id,cwd:e.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:t,action:n?"moved":"dismiss",...s??{}}}var kv=50;async function Dv(){let e=await Jr();if(!e)throw new Error("no ticketing integration configured");return ds(e)}function uB(e){let t=e&&typeof e=="object"?e:{},n=(Array.isArray(t.issues)?t.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=t.epic&&String(t.epic.name||"").trim()?{name:String(t.epic.name).trim(),description:t.epic.description?String(t.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>kv)throw new Error(`too many issues (${n.length}); cap is ${kv} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function dB(e,t,n,r){let o=r??await ds(e),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(t.team){let i=s(t.team);if(i)return i}let a=await Vt(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(e.defaultTeam){let i=s(e.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function jv(e,t){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=uB(t),o=await ds(n),s=await dB(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Av();return await I({id:e.id,cwd:e.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 Lv(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await B({id:e.id,cwd:e.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await I({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,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 h=(await ds(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let h=await pm(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await mm(a,{teamId:c.id,title:h.title,description:h.description,projectId:f,priority:h.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:h.title,message:w?.message||String(w)})}await I({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await I({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(h=>h.kind==="issue"),m=null;return i.linkCurrentTrack&&d.length===1&&!await Vt(e.id)&&(m=await fs(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:m?m.key:null}}async function Ga(e,t){let n=await Zr(e);n&&await n.api.createComment(n.cfg,e,t)}async function fB(e,t,n){let r=await Zr(e);if(!r)return null;let o=await dn(t);if(!o)return null;let s=Et(o,n);if(!s)return null;let a=await Va.readFile(wt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var xv=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),ym=new Map;function pB(e,t){if(xv()<=0)return za(e,t);if(ym.has(e))return;let n=setTimeout(()=>{ym.delete(e),Vt(e).then(r=>r?za(e,r):null).catch(()=>{})},xv());n.unref?.(),ym.set(e,n)}var vv=new Map;async function Sv(e,t,n){let r=vv.get(e)??0;Date.now()-r<600*1e3||(vv.set(e,Date.now()),await I({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function mB(e,{resolveTrack:t=null}={}){let n=await Vt(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await vm())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=t?await t(e.trackId):null;if(!s)return;try{await fs(s,o,{source:"chat"})}catch(a){await Sv(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await sr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await sr(e.trackId),await za(e.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 sr(e.trackId),await za(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Ov(n,e.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?`
|
|
493
493
|
|
|
494
|
-
Agent cost for this Track: ${
|
|
494
|
+
Agent cost for this Track: ${aB(o)}.`:"")),await Mv(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await fB(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Ga(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
495
495
|
|
|
496
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
497
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
498
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
496
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Ga(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 Ga(n,`**Bostrat** \u2014 live preview **${r.name}**: ${kl(r.slug)}
|
|
497
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await pB(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await Sv(e.trackId,s?.cwd??null,o)}}function Fv({resolveTrack:e}){tt.on("event",t=>{mB(t,{resolveTrack:e}).catch(()=>{})})}async function Uv(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ja(),r=0;for(let[o,s]of Object.entries(n))await Ve("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}Bo();Yo();import hB from"ws";var Hv=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function xl(e,t,n={}){let r=await fetch(`${Hv()}/${e}`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(n)}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${e}: ${o?.error||`http ${r.status}`}`);return o}var Em=(e,t)=>xl("chat.postMessage",e,t),Qr=(e,t)=>xl("chat.update",e,t);async function Xe(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function wB(e,t,n={}){let r=await fetch(`${Hv()}/${e}`,{method:"POST",headers:{authorization:`Bearer ${t}`,"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 ${e}: ${o?.error||`http ${r.status}`}`);return o}async function Wv(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await wB("files.getUploadURLExternal",e,{filename:t,length:String(n.length)}),l=await fetch(i,{method:"POST",body:n});if(!l.ok)throw new Error(`slack upload: http ${l.status}`);return xl("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var gB=2200;function qv({getToken:e,onEnvelope:t,log:n=()=>{}}){let r=!1,o=null,s=null,a=1e3;async function i(){if(r)return;let l;try{let u=await e();if(!u)throw new Error("app token missing");({url:l}=await xl("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new hB(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=w=>{try{o.send(JSON.stringify(w?{envelope_id:f.envelope_id,payload:w}:{envelope_id:f.envelope_id}))}catch{}},m=!1,h=setTimeout(()=>{m=!0,d()},gB);Promise.resolve().then(()=>t(f)).then(w=>{clearTimeout(h),!m&&(m=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(h),m||(m=!0,d()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var Vv=()=>process.env.BOSTRAT_STATE_DIR||Sl.join(process.env.HOME||bB.tmpdir(),".bostrat"),Om=()=>Sl.join(Vv(),"slack.json"),vl=null,_m=null,Im=Promise.resolve();async function Mm(){return vl||(_m??=Ya.readFile(Om(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>vl=e).finally(()=>_m=null),_m)}async function Kv(){let e=JSON.stringify({threads:vl},null,2),t=`${Om()}.${process.pid}.tmp`;await Ya.mkdir(Vv(),{recursive:!0}),await Ya.writeFile(t,e),await Ya.rename(t,Om())}async function _t(){Im=Im.then(Kv,Kv),await Im.catch(()=>{})}async function Nm(e){return(await Mm())[e]??null}async function kB(e,t){let n=await Mm();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var ne=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function Xa(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function xB(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return Xa(n.replace(/\n+/g," "),t)}function zv(e){let t=`*${ne(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${ne(e.repoLabel)} \xB7 \`${ne(e.branch||"?")}\` \xB7 lead *${ne(e.lead)}*`,r=e.kickoff?`> ${ne(Xa(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
498
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${bl(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Cm(e,t){await Qr(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:zv(t)})}async function ir(e,t,n,r=void 0){return Em(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var Gv=new Set;function ar(e){Gv.add(e),e.catch(()=>{}).finally(()=>Gv.delete(e))}var ve=e=>({response_type:"ephemeral",text:e}),vB="*/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._",SB=["claude","cursor","codex"];function Rm(e,t){let n=(t||"").toLowerCase(),r=e.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 $m=(e,t)=>ve(`No live Track matches \u201C${ne(t)}\u201D. Live: ${e.map(n=>`\`${ne(n.name)}\``).join(" \xB7 ")||"none"}`);async function Jv(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=t.maxConcurrent){await Xe(o,{...ve(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await Xe(o,{...ve("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:t.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?Sl.basename(c.base):"fresh workspace"),lead:"claude"},u=await Em(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:zv(l),unfurl_links:!1});l.ts=u.ts,(await Mm())[i.id]=l,await _t(),await Xe(o,{...ve(`\u2713 *${ne(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Xe(o,{...ve(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function TB(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Nm(n.id);s&&(s.archivedAt=Date.now(),await _t(),await Cm(t,s).catch(()=>{})),await Xe(r,{...ve(`\u2713 *${ne(n.name)}* archived.`),replace_original:!0})}catch(s){await Xe(r,{...ve(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function AB(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await Xe(o,{...ve(`\u2713 Handing *${ne(n.name)}* to ${ne(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Xe(o,{...ve(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function EB(e,t,{track:n,responseUrl:r}){let o=e.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 Xe(r,{...ve(`\u2713 Stopped *${ne(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 Xe(r,{...ve(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function _B(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:Xa(`${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:e}),text:{type:"mrkdwn",text:`Repo for *${ne(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function IB(e){let t=await e.listTracks(),n=t.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?Sl.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 *${ne(s.name)}* \u2014 ${ne(i)}${a.branch?` \`${ne(a.branch)}\``:""} \xB7 lead ${ne(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
499
499
|
`)+(o?`
|
|
500
|
-
_${o} archived_`:"")}async function
|
|
501
|
-
\u2713 *${
|
|
502
|
-
\u2713 _answered_`}}]})}async function
|
|
503
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
504
|
-
`)){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 OB(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 CB=new Set(["Write","Edit","NotebookEdit"]),MB=220,NB=160;function PB(t,e){let n=t?.tool_name??null,r={tool:n},o=t?.tool_input;if(!o||typeof o!="object")return r;if(CB.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,MB)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=dr(o.command.trim().replace(/\s+/g," ")).slice(0,NB));return r}function BB(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?ze(e.cwd,e.sessionId):e?.cwd?gu(e.cwd):null}async function Lv(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=bc(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:PB(t,o)});break;case"Stop":await DB(r,o,t,i);break;default:break}return a}async function DB(t,e,n,r){let o=[],s=BB(n,e);if(s)try{o=$B(await RB.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=OB(await B(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 h of Object.keys(p.tokens))p.tokens[h]+=d.tokens[h];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:Nn(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}})}ys();st();oc();import FB from"node:fs";import Rm from"node:fs/promises";import UB from"node:http";import ss from"node:path";import yl from"node:os";import{createRequire as HB}from"node:module";import $m from"ws";import jB from"node:path";import LB from"node:os";var qa=process.env.BOSTRAT_STATE_DIR||jB.join(process.env.HOME||LB.tmpdir(),".bostrat");function Im(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 Fv(t=process.env){if(!t.BOSTRAT_PLANE_URL)return null;let e=Im(t);return e==="127.0.0.1"||e==="::1"||e==="localhost"?null:{host:e}}function Uv(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 wl=(()=>{try{return HB(import.meta.url)("../package.json").version||null}catch{return null}})(),WB=process.env.BOSTRAT_STATE_DIR||ss.join(process.env.HOME||yl.tmpdir(),".bostrat");function qB(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 KB({home:t=process.env.HOME||yl.homedir(),env:e=process.env}={}){let n=r=>{try{return FB.statSync(r),!0}catch{return!1}};return{claude:n(ss.join(t,".claude",".credentials.json")),codex:n(ss.join(e.CODEX_HOME||ss.join(t,".codex"),"auth.json"))}}var jt=1,Hv=1e3,GB=3e4,VB=6e4,zB=1500,JB=500,gl=100,YB=2e3,XB=25e3,ZB=6e4,QB=6e4,tD=6e4;function Wv({url:t,token:e,nodeId:n=yl.hostname(),name:r=yl.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=QB,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=Uv(t);let d=l.replace(/^http/,"ws"),f=new Map,p=new Map,h=ss.join(WB,"plane-link","cursor.json"),w={},y={},k=new Map,x=null,A=!1,R=!1,V=Hv,M=null,N=null,T=null,D=null,F=null,ct=!1,Q=0;async function _(){try{w=JSON.parse(await Rm.readFile(h,"utf8"))}catch{w={}}}function U(){ct=!0,!T&&(T=setTimeout(async()=>{if(T=null,!!ct){ct=!1;try{await Rm.mkdir(ss.dirname(h),{recursive:!0}),await Rm.writeFile(h,JSON.stringify(w))}catch(b){u.error("[plane-link] cursor flush failed:",b?.message||b)}}},YB),T.unref?.())}function j(b){return!x||x.readyState!==$m.OPEN?!1:(x.send(JSON.stringify(b)),!0)}function at(b){let E=Date.now();for(let{trackId:O,event:P}of b){let wt=k.get(P.id);k.set(P.id,{trackId:O,seq:P.seq,event:P,sentAt:E,tries:(wt?.tries??0)+1})}return j({v:jt,ch:"events",type:"append",events:b})}function Et(b){let E=null;for(let O of k.values())O.trackId===b&&(E==null||O.seq<E)&&(E=O.seq);return E}function Lt(b){let E=y[b];if(E==null)return;let O=Et(b),P=O==null?E:Math.min(E,O-1);P>(w[b]??0)&&(w[b]=P,U())}function it(){if(!A)return;let b=Date.now(),E=[...k.values()].filter(O=>O.event&&b-O.sentAt>=ZB);if(E.length){E.sort((O,P)=>O.trackId===P.trackId?O.seq-P.seq:O.trackId<P.trackId?-1:1),u.log(`[plane-link] re-sending ${E.length} unacked event(s) the plane never confirmed`);for(let O=0;O<E.length;O+=gl){let P=E.slice(O,O+gl).map(wt=>({trackId:wt.trackId,event:wt.event}));if(!at(P))return}}}async function g(){if(!i)return;let b;try{b=await i()}catch(E){u.error("[plane-link] metrics sample failed:",E?.message||E);return}b&&j({v:jt,ch:"ctl",type:"metrics",metrics:b})}function m(){i&&(F&&clearInterval(F),g(),F=setInterval(g,c),F.unref?.())}async function S(){let b;try{b=await o()}catch(E){u.error("[plane-link] backfill listTracks failed:",E?.message||E);return}j({v:jt,ch:"ctl",type:"tracks",ids:b.filter(E=>!E.transferredTo).map(E=>E.id).filter(E=>typeof E=="string")});for(let E of b){if(!A)return;let O;try{O=await B(E)}catch{continue}let P=w[E.id]??0,wt=O.filter(mt=>mt.seq>P&&typeof mt.id=="string");for(let mt=0;mt<wt.length;mt+=gl){let Wt=wt.slice(mt,mt+gl).map(le=>({trackId:E.id,event:le}));if(!at(Wt))return}}}function $(b){!A||!b?.trackId||typeof b.id!="string"||at([{trackId:b.trackId,event:b}])}let lt=new Map;async function ht(b){let{reqId:E,method:O,path:P,headers:wt={},body:mt}=b,Wt=Object.fromEntries(Object.entries(wt).filter(([,_e])=>_e!=null)),le=new AbortController;lt.set(E,le);try{if(b.host){await vt(b,Wt,le);return}let _e=await fetch(l+P,{method:O,headers:Wt,body:mt?Buffer.from(mt,"base64"):void 0,signal:le.signal}),bn=Object.fromEntries(_e.headers);for(let ue of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete bn[ue];if(bn["content-type"]||(bn["content-type"]="application/octet-stream"),j({v:jt,ch:"control",type:"http_head",reqId:E,status:_e.status,headers:bn}),_e.body){for await(let ue of _e.body)if(!j({v:jt,ch:"control",type:"http_data",reqId:E,data:Buffer.from(ue).toString("base64")}))break}j({v:jt,ch:"control",type:"http_end",reqId:E,error:null})}finally{lt.delete(E)}}function vt(b,E,O){let{reqId:P,method:wt,path:mt,body:Wt}=b,le=new URL(l);return new Promise(_e=>{let bn=UB.request({host:le.hostname,port:le.port,method:wt,path:mt,headers:{...E,host:b.host},signal:O.signal},ue=>{let{connection:sL,"keep-alive":aL,"transfer-encoding":iL,upgrade:cL,...NT}=ue.headers;j({v:jt,ch:"control",type:"http_head",reqId:P,status:ue.statusCode||502,headers:NT}),ue.on("data",PT=>{j({v:jt,ch:"control",type:"http_data",reqId:P,data:Buffer.from(PT).toString("base64")})||ue.destroy()}),ue.on("end",()=>{j({v:jt,ch:"control",type:"http_end",reqId:P,error:null}),_e()}),ue.on("error",()=>{j({v:jt,ch:"control",type:"http_end",reqId:P,error:"view upstream error"}),_e()})});bn.on("error",ue=>{j({v:jt,ch:"control",type:"http_end",reqId:P,error:ue?.message||"view request failed"}),_e()}),Wt&&bn.write(Buffer.from(Wt,"base64")),bn.end()})}function Ct(){if(R)return;x=new $m(t,{headers:{authorization:`Bearer ${e}`}}),x.on("open",async()=>{let E=null;try{E=await s?.()||null}catch{E=null}j({v:jt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:wl,providers:KB(),...E?{agents:E}:{}})}),x.on("message",E=>{let O;try{O=JSON.parse(E)}catch{return}if(O.ch==="ctl"&&O.type==="hello_ok"){A=!0,V=Hv,M=null,u.log(`[plane-link] connected to plane as ${n}`),O.minNodeVersion&&wl&&qB(wl,O.minNodeVersion)&&u.error(`[plane-link] this node is bostrat-node ${wl}; the plane asks for \u2265${O.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),D&&clearInterval(D),D=setInterval(()=>{j({v:jt,ch:"ctl",type:"ping"}),it()},XB),D.unref?.(),m(),S().catch(P=>u.error("[plane-link] backfill failed:",P?.message||P));return}if(O.ch==="ctl"&&O.type==="usage_caps"){rc(O.caps??{},{source:"plane",updatedAt:O.updatedAt??Date.now()}).then(P=>u.log(`[plane-link] spend caps ${Object.keys(P.caps).length?`updated (${Object.keys(P.caps).join(", ")})`:"cleared"} from plane`)).catch(P=>u.error("[plane-link] spend-caps push rejected:",P?.message||P));return}if(O.ch==="ctl"&&O.type==="transferred"&&Array.isArray(O.tracks)){Promise.resolve().then(()=>a?.(O.tracks)).catch(P=>u.error("[plane-link] transfer notice failed:",P?.message||P));return}if(O.ch==="events"&&O.type==="ack"&&Array.isArray(O.ids)){let P=new Set;for(let wt of O.ids){let mt=k.get(wt);mt&&(k.delete(wt),(y[mt.trackId]??0)<mt.seq&&(y[mt.trackId]=mt.seq),P.add(mt.trackId))}for(let wt of P)Lt(wt);return}if(O.ch==="control"&&O.type==="http_open"){ht(O).catch(P=>j({v:jt,ch:"control",type:"http_end",reqId:O.reqId,error:P?.message||"tunnel error"}));return}if(O.ch==="control"&&O.type==="http_cancel"){lt.get(O.reqId)?.abort();return}if(O.ch==="attach"&&O.type==="open"){let P=new $m(d+O.path,Array.isArray(O.protocols)?O.protocols:[],O.host?{headers:{host:O.host}}:void 0);f.set(O.streamId,P),p.set(O.streamId,[]),P.on("open",()=>{let mt=p.get(O.streamId)||[];p.delete(O.streamId);for(let{buf:Wt,bin:le}of mt)P.send(Wt,{binary:le})}),P.on("message",(mt,Wt)=>{let le=Wt?mt:Buffer.from(String(mt));j({v:jt,ch:"attach",type:"data",streamId:O.streamId,data:le.toString("base64"),...Wt?{bin:!0}:{}})});let wt=()=>{p.delete(O.streamId),f.delete(O.streamId)&&j({v:jt,ch:"attach",type:"close",streamId:O.streamId})};P.on("close",wt),P.on("error",wt);return}if(O.ch==="attach"&&O.type==="data"){let P=f.get(O.streamId);if(!P)return;let wt=Buffer.from(O.data,"base64"),mt=O.bin===!0,Wt=p.get(O.streamId);Wt?Wt.push({buf:wt,bin:mt}):P.send(wt,{binary:mt});return}if(O.ch==="attach"&&O.type==="close"){let P=f.get(O.streamId);f.delete(O.streamId),p.delete(O.streamId),P?.close();return}});let b=()=>{A&&u.log("[plane-link] disconnected from plane"),A=!1,D&&(clearInterval(D),D=null),F&&(clearInterval(F),F=null),k.clear(),y={};for(let P of f.values())P.close();f.clear(),p.clear();for(let P of lt.values())P.abort();if(lt.clear(),R||N)return;M==null&&(M=Date.now());let E=Date.now()-M<VB,O=E?zB+Math.random()*JB:V;N=setTimeout(()=>{N=null,Ct()},O),N.unref?.(),E||(V=Math.min(V*2,GB))};x.on("close",b),x.on("error",E=>{let O=/Unexpected server response: (\d+)/.exec(E?.message||"")?.[1];(O==="401"||O==="403")&&Date.now()-Q>=tD&&(Q=Date.now(),u.error(`[plane-link] plane refused this node's credentials (HTTP ${O}) \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 Qt.on("event",$),_().then(Ct),{stop:()=>{R=!0,Qt.off("event",$),N&&clearTimeout(N),T&&clearTimeout(T),D&&clearInterval(D),F&&clearInterval(F),j({v:jt,ch:"ctl",type:"bye",reason:"shutdown"}),x?.close()},status:()=>({connected:A,nodeId:n,cursor:{...w},inflight:k.size})}}import eD from"node:fs";import qv from"node:fs/promises";import Ka from"node:os";import nD from"node:path";Uf();Gt();var rD=1024*1024,bl=t=>Math.round(t/rD);async function oD(t){try{let e=await qv.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:bl(r),diskTotalMb:bl(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function sD(){try{return(await be()).filter(e=>e.isBostrat).length}catch{return null}}async function aD(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await qv.readdir(nD.join(qa,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function Kv({stateDir:t=qa,now:e=Date.now}={}){let n=Ka.totalmem(),[r,o,s,a]=await Promise.all([Pc().catch(()=>null),oD(eD.existsSync(t)?t:Ka.tmpdir()),sD(),aD()]),i=Ka.cpus()?.length||null,c=Ka.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?bl(n-r):null,memTotalMb:bl(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Ka.uptime())}}st();ju();wi();st();kr();function Om(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 Gv=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Vv=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},kl=t=>Math.round(t*1e4)/1e4;async function zv(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=Gv(),d=0,f=[];for(let p of t||[]){let h=await B(p),w=Pn(h,{boxLabels:Om(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);Vv(u,w.totalTokens);for(let[y,k]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:Gv()},n[y].usd=kl(n[y].usd+k.usd),n[y].events+=k.events,Vv(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=kl(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,h)=>h.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:kl(o),asRecordedUsd:kl(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 er=t=>`$${t.toFixed(4)}`,as=(t,e)=>String(t).padEnd(e);function Jv(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 ${er(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 ${er(e.asRecordedUsd)} \xB7 current-rates total ${er(e.totalUsd)} (${e.totalUsd>=e.asRecordedUsd?"+":""}${er(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(` ${as(a,26)} ${as(er(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(` ${as(a.name||a.id,30)} ${er(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${as(c.label,40)} ${as(er(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))}${as(er(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
505
|
-
`)}mf();oc();st();Gd();import dD from"node:fs/promises";import fD from"node:path";Du();kr();var xl=t=>t.payload?.ts??t.ts,iD=12e4,nr=t=>Math.round(t*1e4)/1e4,cD=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function lD(t,{since:e}){let{track:n,events:r,brief:o}=t,s=x=>xl(x)>=e,a=yr([Hs(n?.name),Hs(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 A=yr(x.payload.task);A&&i.set(x.boxId,A)}let c=new Map,l=x=>{let A=x||"unattributed";return c.has(A)||c.set(A,{segments:[u(null)],lastTs:null,provider:null}),c.get(A)},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}),d=[...r].sort((x,A)=>xl(x)-xl(A)),f=0,p=0,h=0,w={turns:0};for(let x of d){let A=l(x.boxId),R=A.segments[A.segments.length-1],V=xl(x);switch(A.lastTs!=null&&V>A.lastTs&&s(x)&&(R.activeMs+=Math.min(V-A.lastTs,iD)),A.lastTs=V,x.provider&&(A.provider=x.provider),x.type){case"turn.user":{let M=x.payload?.text;if(x.source==="hook"||!String(M||"").trim())break;let N=u(yr(M));A.segments.push(N),s(x)&&(N.turns+=1);break}case"peer.message":{let M=x.payload?.text;if(!String(M||"").trim())break;let N=u(yr(M));A.segments.push(N),s(x)&&(N.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let M=x.payload?.tool;M&&(R.tools[M]=(R.tools[M]||0)+1),s(x)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:M,tokens:N,model:T,unpriced:D}=Je(x);D&&(w.turns+=1),R.usd+=M,N&&(R.tokens.input+=N.input,R.tokens.output+=N.output,R.tokens.cacheRead+=N.cacheRead,R.tokens.cacheWrite+=N.cacheWrite),R.models[T]=(R.models[T]||0)+M,x.provider&&(R.provider=x.provider);break}case"handoff.done":s(x)&&(f+=1);break;case"subtask.merged":s(x)&&(p+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let y=new Map,k=!1;for(let[x,A]of c)for(let R of A.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let M=R.textCat?.category??i.get(x)?.category??Ww(R.tools)?.category??a?.category??"uncategorized";y.has(M)||y.set(M,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let N=y.get(M);N.usd+=R.usd,N.activeMs+=R.activeMs,N.turns+=R.turns,N.toolCalls+=R.toolCalls;for(let[D,F]of Object.entries(R.models))N.models[D]=(N.models[D]||0)+F;let T=R.provider||A.provider;T&&(N.providers[T]=(N.providers[T]||0)+R.usd),T==="cursor"&&R.usd===0&&(k=!0)}return{perCategory:y,handoffs:f,merged:p,failed:h,sawCursorActivity:k,unpricedTurns:w.turns}}function Xv(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:A,merged:R,failed:V,sawCursorActivity:M,unpricedTurns:N}=lD(w,{since:r});M&&(i=!0),c+=N;let T={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:A,subtasksMerged:R,subtasksFailed:V};for(let[D,F]of x){T.usd+=F.usd,T.activeMs+=F.activeMs,T.turns+=F.turns,T.categories[D]={usd:nr(F.usd),activeMs:F.activeMs};for(let[_,U]of Object.entries(F.models))T.models[_]=nr((T.models[_]||0)+U);for(let[_,U]of Object.entries(F.providers))T.providers[_]=nr((T.providers[_]||0)+U);o.has(D)||o.set(D,cD());let ct=o.get(D);ct.usd+=F.usd,ct.activeMs+=F.activeMs,ct.turns+=F.turns,ct.toolCalls+=F.toolCalls;for(let[_,U]of Object.entries(F.models))ct.models[_]=(ct.models[_]||0)+U;for(let[_,U]of Object.entries(F.providers))ct.providers[_]=(ct.providers[_]||0)+U;let Q=ct.byTrack.get(y?.id)||{usd:0,activeMs:0};Q.usd+=F.usd,Q.activeMs+=F.activeMs,ct.byTrack.set(y?.id,Q)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=nr(T.usd),T.dominantCategory=uD(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),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])),h=[...o.entries()].map(([w,y])=>({category:w,sharePct:f>0?Math.round((d==="usd"?y.usd:y.activeMs)/f*100):0,shareBasis:d,usd:nr(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:Yv(y.models),providers:Yv(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:nr(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:nr(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var uD=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},Yv=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,nr(n)]));var is={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function pD(t){let e=String(t||"7d");return e in is?{key:e,ms:is[e]}:{key:"7d",ms:is["7d"]}}function mD(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=ic(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 hD(t){if(!t?.cwd)return null;try{let n=(await dD.readFile(fD.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 Zv(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}:pD(e),d=[];for(let f of t||[]){let p=await B(f),h=mD(f,p);i&&c<o&&(h=h.filter(w=>(w.ts??0)<=c)),d.push({track:f,events:h,brief:await hD(f)})}return{window:l,...Xv(d,{windowMs:u,now:c})}}st();kr();var rr=36e5,nS=24*rr,wD=400,gD=12,cs=t=>Math.round(t*1e4)/1e4;function yD({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 is?String(t||"7d"):"7d",i=is[a];return{key:a,since:i==null?null:r-i,until:r}}var bD=(t,e)=>t!=null&&e-t<=48*rr?rr:nS;function Qv(t,e){if(e===rr)return Math.floor(t/rr)*rr;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function kD(t,e){if(e===rr)return t+rr;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},xD=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,cs(n)]));async function rS(t,e={}){let n=e.now??Date.now(),r=yD({...e,now:n}),o=r.since==null?nS:bD(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=tS(),d=0,f=0,p=0,h=0,w=0,y=new Set,k=new Map,x=[],A=[],R=[],V=null;for(let T of t||[]){let F=(await B(T)).filter(_=>_.type==="cost.recorded");if(!F.length){T.archivedAt||R.push(T.name||T.id);continue}let ct=[];for(let _ of F){let U=_.ts??0;s(U)?ct.push(_):i(U)&&(w+=Je(_).usd)}if(!ct.length)continue;let Q=Pn(ct,{boxLabels:Om(T)});d+=Q.totalUsd,f+=Q.turns,p+=Q.repricedTurns||0,h+=Q.unpricedTurns||0;for(let _ of Q.unpricedModels||[])y.add(_);eS(u,Q.totalTokens);for(let[_,U]of Object.entries(Q.byModel))c[_]||={usd:0,events:0,tokens:tS()},c[_].usd=cs(c[_].usd+U.usd),c[_].events+=U.events,eS(c[_].tokens,U.tokens);for(let[_,U]of Object.entries(Q.byProvider))l[_]||={usd:0,events:0},l[_].usd=cs(l[_].usd+U.usd),l[_].events+=U.events;for(let _ of ct){let{usd:U}=Je(_);if(!U)continue;let j=Qv(_.ts??0,o),at=k.get(j)??{t:j,total:0,byProvider:{}};at.total+=U;let Et=_.provider||"unknown";at.byProvider[Et]=(at.byProvider[Et]||0)+U,k.set(j,at),(V==null||j<V)&&(V=j)}x.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:Q.totalUsd,turns:Q.turns,providers:Object.keys(Q.byProvider)});for(let[_,U]of Object.entries(Q.byBox))A.push({trackId:T.id,trackName:T.name||T.id,boxId:_,...U})}let M=r.since!=null?Qv(r.since,o):V,N=[];for(let T=M,D=0;T!=null&&T<r.until&&D<wD;T=kD(T,o),D++){let F=k.get(T);N.push(F?{t:T,total:cs(F.total),byProvider:xD(F.byProvider)}:{t:T,total:0,byProvider:{}})}return x.sort((T,D)=>D.usd-T.usd),A.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:N,fleet:{totalUsd:cs(d),turns:f,repricedTurns:p,unpricedTurns:h,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:R},prior:a!=null?{totalUsd:cs(w),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:A.slice(0,gD)}}qt();import ls from"node:fs/promises";import vD from"node:fs";import Ut from"node:path";import SD from"node:os";import{execFile as TD}from"node:child_process";import{promisify as AD}from"node:util";var ED=AD(TD),oS=process.env.BOSTRAT_STATE_DIR||Ut.join(process.env.HOME||SD.tmpdir(),".bostrat"),vl=!1,Cm=null;async function Mm(t,e){let n=await ls.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await ls.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ls.mkdir(Ut.dirname(e),{recursive:!0}),await ls.copyFile(t,e),!0)}var _D=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 Sl(t,e,{skip:n=null}={}){let r=0,o=await ls.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(_D.has(s.name)||n?.has(s.name))continue;let a=Ut.join(t,s.name);s.isDirectory()?r+=await Sl(a,Ut.join(e,s.name)):s.isFile()&&(r+=await Mm(a,Ut.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 ls.mkdir(e,{recursive:!0});let o=0;o+=await Mm(Ut.join(t,"tracks.json"),Ut.join(e,"tracks.json"))?1:0,o+=await Mm(Ut.join(t,"events.jsonl"),Ut.join(e,"events.jsonl"))?1:0,o+=await Sl(Ut.join(t,"events"),Ut.join(e,"events")),o+=await Sl(Ut.join(t,"memory"),Ut.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Sl(Ut.join(u.cwd,".bostrat"),Ut.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>ED("git",["-C",e,...u]);vD.existsSync(Ut.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 Jl(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||vl))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,vl&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),vl=!1,Cm=null}catch(u){vl=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
506
|
-
`).slice(-3).join(" | ").slice(0,300);
|
|
507
|
-
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,
|
|
508
|
-
`).map(s=>s.replace(/\s+$/,"")),n=
|
|
509
|
-
`),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
|
|
510
|
-
`,{mode:384}),
|
|
500
|
+
_${o} archived_`:"")}async function RB(e,t){let n=await Yr();if(!n)return ve("Slack integration not configured.");let r=e.deps,o=(t.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return ve("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?(ar(Jv(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),ve(`Starting *${ne(u)}* on ${ne(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:_B(u,i)}}case"archive":{if(!a.length)return ve("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Rm(await r.listTracks(),a.join(" "));return i?(ar(TB(e,n,{track:i,responseUrl:t.response_url})),ve(`Archiving *${ne(i.name)}*\u2026`)):$m(c,a.join(" "))}case"handoff":{let i=r.knownAgents??SB,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return ve(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=Rm(await r.listTracks(),l);return u?(ar(AB(e,n,{track:u,agent:c,responseUrl:t.response_url})),ve(`Handing *${ne(u.name)}* to ${ne(c)}\u2026`)):$m(f,l)}case"stop":{if(!a.length)return ve("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Rm(await r.listTracks(),a.join(" "));return i?(ar(EB(e,n,{track:i,responseUrl:t.response_url})),ve(`Stopping *${ne(i.name)}*\u2026`)):$m(c,a.join(" "))}case"list":return ve(await IB(r));default:return ve(vB)}}async function $B(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Nm(o);try{if(await e.deps.answerPrompt(o,{promptId:s,answers:[a]}),i?.prompt?.promptId===s){let{ts:c,question:l}=i.prompt;delete i.prompt,await _t(),await Qr(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ne(l||"question")}~
|
|
501
|
+
\u2713 *${ne(a)}*`}}]}).catch(()=>{})}}catch(c){await Xe(n.response_url,ve(`Couldn't answer: ${c?.message||c}`))}}async function OB(e,t,n,r){let{trackId:o,offerId:s,action:a}=r,i=e.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=Array.isArray(r.actions)?No.filter(d=>r.actions.includes(d)):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,Po(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 Xe(n.response_url,ve(`Couldn't act on the offer: ${c?.message||c}`))}}async function CB(e,t,n,r){let{trackId:o,offerId:s,choice:a}=r;try{let i=await e.deps.resolveTrack(o);if(!i)throw new Error("track not found");await e.deps.answerPrExists(i,{offerId:s,choice:a})}catch(i){await Xe(n.response_url,ve(`Couldn't act on the offer: ${i?.message||i}`))}}async function MB(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await Yr();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 Xe(t.response_url,{...ve(`Starting *${ne(s)}*\u2026`),replace_original:!0}),ar(Jv(e,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:t.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?ar($B(e,r,t,o)):n.action_id==="bostrat-ship"?ar(OB(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&ar(CB(e,r,t,o))}async function NB(e,t){if(!t||t.type!=="message"||t.bot_id||t.subtype||!t.thread_ts||t.thread_ts===t.ts||!t.text?.trim())return;let n=await kB(t.channel,t.thread_ts);if(!n)return;let r=await Yr();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await ir(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function PB(e){return async t=>{if(t.type==="slash_commands")return RB(e,t.payload);if(t.type==="interactive"){await MB(e,t.payload);return}if(t.type==="events_api"){await NB(e,t.payload?.event);return}}}var BB=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 DB(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${ne(t.question||"The lead needs an answer")}`}}],r=(t.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:Xa(String(o),75)},value:JSON.stringify({trackId:e,promptId:t.promptId,label:String(o)})}))}):n.push({type:"context",elements:[{type:"mrkdwn",text:"_reply in this thread to answer_"}]}),n}async function jB(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await Qr(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ne(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await ir(e,t,r.question||"The lead needs an answer",DB(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await _t()}async function LB(e,t,n){let r=n.payload||{};if(!t.prompt||r.promptId&&t.prompt.promptId!==r.promptId)return;let{ts:o,question:s}=t.prompt;delete t.prompt,await _t(),await Qr(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ne(s||"question")}~
|
|
502
|
+
\u2713 _answered_`}}]})}async function FB(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=(i,c,l,u)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i,...u?{actions:u}:{}}),...l?{style:l}:{}}),s;if(!Array.isArray(r.available))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")];else{let i=r.available,c=Array.isArray(r.defaults)&&r.defaults.length?r.defaults:i,l={commit:"Commit",rebase:"rebase",push:"push",pr:r.prState==="open"?"open new PR":"open PR"},u=c.map(f=>l[f]).filter(Boolean).join(" & ");s=[o(c.includes("pr")?"pr":"push",u||"Ship","primary",c)],i.includes("pr")&&!c.includes("pr")&&s.push(o("pr","Open new PR",void 0,["pr"])),s.push(o("dismiss","Not now"))}let a=await ir(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${ne(Xa(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await _t()}async function UB(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await _t();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 Qr(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function HB(e,t,n){let r=n.payload||{};if(!r.offerId||t.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 ir(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${ne(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")]}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await _t()}async function WB(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await _t();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await Qr(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function qB(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??KB)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Wv(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${ne(o.name)}*${a}${o.group?` (group \`${ne(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${ne(a.name)}*`).join(", ");await ir(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${ne(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function KB(e,t){let n=await dn(e);if(!n)return null;let r=Et(n,t);if(!r)return null;let o=await Ya.readFile(wt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function GB(e,t){if(!BB.has(t.type))return;let n=await Nm(t.trackId);if(!n)return;let r=await Yr();if(!r)return;let o=t.payload||{};switch(t.type){case"turn.user":if(n.kickoff||t.actor!=="user"||!o.text)return;n.kickoff=o.text,await _t(),await Cm(r,n);return;case"turn.agent":{if(!o.text)return;let s=xB(o.text);if(!s)return;await ir(r,n,s,[{type:"section",text:{type:"mrkdwn",text:ne(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${bl(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return jB(r,n,t);case"prompt.answered":return LB(r,n,t);case"pr.offered":return FB(r,n,t);case"pr.offer.answered":return UB(r,n,t);case"pr.exists.offered":return HB(r,n,t);case"pr.exists.answered":return WB(r,n,t);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await ir(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 _t(),await Cm(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await ir(r,n,`Live preview ${o.name}: ${kl(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${ne(o.name)}*: ${kl(o.slug)}
|
|
503
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return qB(r,n,t,e.deps)}}function Yv(e){let t={deps:e};tt.on("event",s=>{GB(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Yr()||(r&&clearInterval(r),r=null,n=qv({getToken:async()=>(await Yr())?.appToken,onEnvelope:PB(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}vt();le();Yt();gf();ea();import VB from"node:fs/promises";function zB(e){let t=new Map;for(let n of String(e||"").split(`
|
|
504
|
+
`)){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&&t.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...t.values()]}function JB(e){let t=new Set;for(let n of e)if(n.type==="cost.recorded"){n.payload?.messageId&&t.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])t.add(r)}return t}var YB=new Set(["Write","Edit","NotebookEdit"]),XB=220,ZB=160;function QB(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(YB.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=t?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,XB)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=yr(o.command.trim().replace(/\s+/g," ")).slice(0,ZB));return r}function eD(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Zt(t.cwd,t.sessionId):t?.cwd?Tu(t.cwd):null}async function Xv(e,t){let n=await t(e?.session_id,e?.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(e?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:e.session_id}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=Cc(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:QB(e,o)});break;case"Stop":await tD(r,o,e,i);break;default:break}return a}async function tD(e,t,n,r){let o=[],s=eD(n,t);if(s)try{o=zB(await VB.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??t?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=JB(await B(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=f.model||"unknown",m=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let h of Object.keys(m.tokens))m.tokens[h]+=f.tokens[h];m.messageIds.push(f.messageId),u.set(d,m)}for(let[f,d]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:Fn(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}})}As();le();fc();import nS from"node:fs";import Bm from"node:fs/promises";import oD from"node:http";import xn from"node:path";import Al from"node:os";import{createRequire as sD}from"node:module";import Dm from"ws";import nD from"node:path";import rD from"node:os";var Za=process.env.BOSTRAT_STATE_DIR||nD.join(process.env.HOME||rD.tmpdir(),".bostrat");function Pm(e=process.env){let t=(e.BOSTRAT_HOST||"").trim();if(t!=="all")return t||(e.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function Zv(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Pm(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function Qv(e){let t=String(e||"").trim();if(!t)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`wss://${t}`,r;try{r=new URL(n)}catch{return t}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()):t}var kn=(()=>{try{return sD(import.meta.url)("../package.json").version||null}catch{return null}})(),aD=process.env.BOSTRAT_STATE_DIR||xn.join(process.env.HOME||Al.tmpdir(),".bostrat");function iD({env:e=process.env,pkgDir:t=xn.resolve(xn.dirname(new URL(import.meta.url).pathname),".."),exists:n=nS.existsSync}={}){let r=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(r)?r:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(xn.join(t,"..",".git"))?"git-checkout":"dev"}function jm(e,t){let n=String(e).split(".").map(Number),r=String(t).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 eS(e){switch(e){case"npm-global":return"npm install -g bostrat-node && restart the bostrat-node service";case"git-checkout":return"scripts/deploy-pi.sh (or The Bakery \u2192 Deploy to Pi \u2192 bostrat)";case"container":return"Links \u2192 Fly \u2192 Update (or the plane's auto-update while the node sleeps)";default:return"update this node's bostrat-node checkout/install"}}function cD({home:e=process.env.HOME||Al.homedir(),env:t=process.env}={}){let n=r=>{try{return nS.statSync(r),!0}catch{return!1}};return{claude:n(xn.join(e,".claude",".credentials.json")),codex:n(xn.join(t.CODEX_HOME||xn.join(e,".codex"),"auth.json"))}}var Ue=1,tS=1e3,lD=3e4,uD=6e4,dD=1500,fD=500,Tl=100,pD=2e3,mD=25e3,hD=6e4,wD=6e4,gD=6e4;function rS({url:e,token:t,nodeId:n=Al.hostname(),name:r=Al.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=wD,onUpdateAvailable:l=null,installKind:u=iD(),selfUrl:f=`http://127.0.0.1:${process.env.PORT||3362}`,log:d=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=Qv(e);let m=f.replace(/^http/,"ws"),h=new Set,w=new Map,y=new Map,b=xn.join(aD,"plane-link","cursor.json"),x={},E={},_=new Map,q=null,C=!1,N=!1,T=tS,W=null,H=null,ae=null,J=null,A=null,j=!1,Q=0;async function fe(){try{x=JSON.parse(await Bm.readFile(b,"utf8"))}catch{x={}}}function Re(){j=!0,!ae&&(ae=setTimeout(async()=>{if(ae=null,!!j){j=!1;try{await Bm.mkdir(xn.dirname(b),{recursive:!0}),await Bm.writeFile(b,JSON.stringify(x))}catch(Y){d.error("[plane-link] cursor flush failed:",Y?.message||Y)}}},pD),ae.unref?.())}function ue(Y){return!q||q.readyState!==Dm.OPEN?!1:(q.send(JSON.stringify(Y)),!0)}function ie(Y){let P=Date.now();for(let{trackId:R,event:M}of Y){let ye=_.get(M.id);_.set(M.id,{trackId:R,seq:M.seq,event:M,sentAt:P,tries:(ye?.tries??0)+1})}return ue({v:Ue,ch:"events",type:"append",events:Y})}function g(Y){let P=null;for(let R of _.values())R.trackId===Y&&(P==null||R.seq<P)&&(P=R.seq);return P}function p(Y){let P=E[Y];if(P==null)return;let R=g(Y),M=R==null?P:Math.min(P,R-1);M>(x[Y]??0)&&(x[Y]=M,Re())}function S(){if(!C)return;let Y=Date.now(),P=[..._.values()].filter(R=>R.event&&Y-R.sentAt>=hD);if(P.length){P.sort((R,M)=>R.trackId===M.trackId?R.seq-M.seq:R.trackId<M.trackId?-1:1),d.log(`[plane-link] re-sending ${P.length} unacked event(s) the plane never confirmed`);for(let R=0;R<P.length;R+=Tl){let M=P.slice(R,R+Tl).map(ye=>({trackId:ye.trackId,event:ye.event}));if(!ie(M))return}}}async function $(){if(!i)return;let Y;try{Y=await i()}catch(P){d.error("[plane-link] metrics sample failed:",P?.message||P);return}Y&&ue({v:Ue,ch:"ctl",type:"metrics",metrics:Y})}function we(){i&&(A&&clearInterval(A),$(),A=setInterval($,c),A.unref?.())}async function ge(){let Y;try{Y=await o()}catch(P){d.error("[plane-link] backfill listTracks failed:",P?.message||P);return}ue({v:Ue,ch:"ctl",type:"tracks",ids:Y.filter(P=>!P.transferredTo).map(P=>P.id).filter(P=>typeof P=="string")});for(let P of Y){if(!C)return;let R;try{R=await B(P)}catch{continue}let M=x[P.id]??0,ye=R.filter(xe=>xe.seq>M&&typeof xe.id=="string");for(let xe=0;xe<ye.length;xe+=Tl){let Ke=ye.slice(xe,xe+Tl).map(dt=>({trackId:P.id,event:dt}));if(!ie(Ke))return}}}function Se(Y){!C||!Y?.trackId||typeof Y.id!="string"||ie([{trackId:Y.trackId,event:Y}])}let $e=new Map;async function v(Y){let{reqId:P,method:R,path:M,headers:ye={},body:xe}=Y,Ke=Object.fromEntries(Object.entries(ye).filter(([,Rt])=>Rt!=null)),dt=new AbortController;$e.set(P,dt);try{if(Y.host){await V(Y,Ke,dt);return}let Rt=await fetch(f+M,{method:R,headers:Ke,body:xe?Buffer.from(xe,"base64"):void 0,signal:dt.signal}),An=Object.fromEntries(Rt.headers);for(let ft of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete An[ft];if(An["content-type"]||(An["content-type"]="application/octet-stream"),ue({v:Ue,ch:"control",type:"http_head",reqId:P,status:Rt.status,headers:An}),Rt.body){for await(let ft of Rt.body)if(!ue({v:Ue,ch:"control",type:"http_data",reqId:P,data:Buffer.from(ft).toString("base64")}))break}ue({v:Ue,ch:"control",type:"http_end",reqId:P,error:null})}finally{$e.delete(P)}}function V(Y,P,R){let{reqId:M,method:ye,path:xe,body:Ke}=Y,dt=new URL(f);return new Promise(Rt=>{let An=oD.request({host:dt.hostname,port:dt.port,method:ye,path:xe,headers:{...P,host:Y.host},signal:R.signal},ft=>{let{connection:vL,"keep-alive":SL,"transfer-encoding":TL,upgrade:AL,...zT}=ft.headers;ue({v:Ue,ch:"control",type:"http_head",reqId:M,status:ft.statusCode||502,headers:zT}),ft.on("data",JT=>{ue({v:Ue,ch:"control",type:"http_data",reqId:M,data:Buffer.from(JT).toString("base64")})||ft.destroy()}),ft.on("end",()=>{ue({v:Ue,ch:"control",type:"http_end",reqId:M,error:null}),Rt()}),ft.on("error",()=>{ue({v:Ue,ch:"control",type:"http_end",reqId:M,error:"view upstream error"}),Rt()})});An.on("error",ft=>{ue({v:Ue,ch:"control",type:"http_end",reqId:M,error:ft?.message||"view request failed"}),Rt()}),Ke&&An.write(Buffer.from(Ke,"base64")),An.end()})}function Tn(){if(N)return;q=new Dm(e,{headers:{authorization:`Bearer ${t}`}}),q.on("open",async()=>{let P=null;try{P=await s?.()||null}catch{P=null}ue({v:Ue,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:kn,installKind:u,providers:cD(),...P?{agents:P}:{}})}),q.on("message",P=>{let R;try{R=JSON.parse(P)}catch{return}if(R.ch==="ctl"&&R.type==="hello_ok"){C=!0,T=tS,W=null,d.log(`[plane-link] connected to plane as ${n}`),R.minNodeVersion&&kn&&jm(kn,R.minNodeVersion)&&d.error(`[plane-link] this node is bostrat-node ${kn}; the plane asks for \u2265${R.minNodeVersion} \u2014 update with: ${eS(u)}`);let M=typeof R.latestNodeVersion=="string"?R.latestNodeVersion:null;if(M&&kn&&jm(kn,M)&&!h.has(M)){h.add(M),d.log(`[plane-link] bostrat-node ${M} is available (this node runs ${kn}) \u2014 ${eS(u)}`);try{l?.({current:kn,latest:M,min:R.minNodeVersion??null,installKind:u,required:!!(R.minNodeVersion&&jm(kn,R.minNodeVersion))})}catch(ye){d.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}J&&clearInterval(J),J=setInterval(()=>{ue({v:Ue,ch:"ctl",type:"ping"}),S()},mD),J.unref?.(),we(),ge().catch(ye=>d.error("[plane-link] backfill failed:",ye?.message||ye));return}if(R.ch==="ctl"&&R.type==="usage_caps"){dc(R.caps??{},{source:"plane",updatedAt:R.updatedAt??Date.now()}).then(M=>d.log(`[plane-link] spend caps ${Object.keys(M.caps).length?`updated (${Object.keys(M.caps).join(", ")})`:"cleared"} from plane`)).catch(M=>d.error("[plane-link] spend-caps push rejected:",M?.message||M));return}if(R.ch==="ctl"&&R.type==="transferred"&&Array.isArray(R.tracks)){Promise.resolve().then(()=>a?.(R.tracks)).catch(M=>d.error("[plane-link] transfer notice failed:",M?.message||M));return}if(R.ch==="events"&&R.type==="ack"&&Array.isArray(R.ids)){let M=new Set;for(let ye of R.ids){let xe=_.get(ye);xe&&(_.delete(ye),(E[xe.trackId]??0)<xe.seq&&(E[xe.trackId]=xe.seq),M.add(xe.trackId))}for(let ye of M)p(ye);return}if(R.ch==="control"&&R.type==="http_open"){v(R).catch(M=>ue({v:Ue,ch:"control",type:"http_end",reqId:R.reqId,error:M?.message||"tunnel error"}));return}if(R.ch==="control"&&R.type==="http_cancel"){$e.get(R.reqId)?.abort();return}if(R.ch==="attach"&&R.type==="open"){let M=new Dm(m+R.path,Array.isArray(R.protocols)?R.protocols:[],R.host?{headers:{host:R.host}}:void 0);w.set(R.streamId,M),y.set(R.streamId,[]),M.on("open",()=>{let xe=y.get(R.streamId)||[];y.delete(R.streamId);for(let{buf:Ke,bin:dt}of xe)M.send(Ke,{binary:dt})}),M.on("message",(xe,Ke)=>{let dt=Ke?xe:Buffer.from(String(xe));ue({v:Ue,ch:"attach",type:"data",streamId:R.streamId,data:dt.toString("base64"),...Ke?{bin:!0}:{}})});let ye=()=>{y.delete(R.streamId),w.delete(R.streamId)&&ue({v:Ue,ch:"attach",type:"close",streamId:R.streamId})};M.on("close",ye),M.on("error",ye);return}if(R.ch==="attach"&&R.type==="data"){let M=w.get(R.streamId);if(!M)return;let ye=Buffer.from(R.data,"base64"),xe=R.bin===!0,Ke=y.get(R.streamId);Ke?Ke.push({buf:ye,bin:xe}):M.send(ye,{binary:xe});return}if(R.ch==="attach"&&R.type==="close"){let M=w.get(R.streamId);w.delete(R.streamId),y.delete(R.streamId),M?.close();return}});let Y=()=>{C&&d.log("[plane-link] disconnected from plane"),C=!1,J&&(clearInterval(J),J=null),A&&(clearInterval(A),A=null),_.clear(),E={};for(let M of w.values())M.close();w.clear(),y.clear();for(let M of $e.values())M.abort();if($e.clear(),N||H)return;W==null&&(W=Date.now());let P=Date.now()-W<uD,R=P?dD+Math.random()*fD:T;H=setTimeout(()=>{H=null,Tn()},R),H.unref?.(),P||(T=Math.min(T*2,lD))};q.on("close",Y),q.on("error",P=>{let R=/Unexpected server response: (\d+)/.exec(P?.message||"")?.[1];(R==="401"||R==="403")&&Date.now()-Q>=gD&&(Q=Date.now(),d.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)`)),Y()})}return tt.on("event",Se),fe().then(Tn),{stop:()=>{N=!0,tt.off("event",Se),H&&clearTimeout(H),ae&&clearTimeout(ae),J&&clearInterval(J),A&&clearInterval(A),ue({v:Ue,ch:"ctl",type:"bye",reason:"shutdown"}),q?.close()},status:()=>({connected:C,nodeId:n,cursor:{...x},inflight:_.size})}}import yD from"node:fs";import oS from"node:fs/promises";import Qa from"node:os";import bD from"node:path";Kf();ze();var kD=1024*1024,El=e=>Math.round(e/kD);async function xD(e){try{let t=await oS.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:El(r),diskTotalMb:El(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function vD(){try{return(await xt()).filter(t=>t.isBostrat).length}catch{return null}}async function SD(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await oS.readdir(bD.join(Za,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function sS({stateDir:e=Za,now:t=Date.now}={}){let n=Qa.totalmem(),[r,o,s,a]=await Promise.all([Wc().catch(()=>null),xD(yD.existsSync(e)?e:Qa.tmpdir()),vD(),SD()]),i=Qa.cpus()?.length||null,c=Qa.loadavg()?.[0]??null;return{ts:t(),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?El(n-r):null,memTotalMb:El(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Qa.uptime())}}le();Ku();Ti();le();_r();function Lm(e){let t={};for(let n of e.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";t[n.name]=`${n.name} (${n.agent||"?"}${r})`}return t}var aS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),iS=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},_l=e=>Math.round(e*1e4)/1e4;async function cS(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=aS(),f=0,d=[];for(let m of e||[]){let h=await B(m),w=Un(h,{boxLabels:Lm(m)});if(w.turns===0){m.archivedAt?f++:d.push(m.name||m.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);iS(u,w.totalTokens);for(let[y,b]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:aS()},n[y].usd=_l(n[y].usd+b.usd),n[y].events+=b.events,iS(n[y].tokens,b.tokens);for(let[y,b]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=_l(r[y].usd+b.usd),r[y].events+=b.events;t.push({id:m.id,name:m.name,usd:w.totalUsd,rollup:w})}return t.sort((m,h)=>h.usd-m.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:_l(o),asRecordedUsd:_l(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:t}}var cr=e=>`$${e.toFixed(4)}`,ps=(e,t)=>String(e).padEnd(t);function lS(e){let{fleet:t,tracks:n}=e,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${e.generatedAt}`),e.pricingBasis&&r.push(`basis: ${e.pricingBasis}`);let o=t.tracksSilentLive||[];r.push(`fleet total ${cr(t.totalUsd)} over ${t.turns} turn(s) \xB7 ${t.tracksWithCost} track(s) with cost, ${t.tracksSilentArchived??t.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),t.repricedTurns&&r.push(`${t.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),t.asRecordedUsd!=null&&Math.abs(t.asRecordedUsd-t.totalUsd)>=.005&&r.push(`as-recorded total ${cr(t.asRecordedUsd)} \xB7 current-rates total ${cr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${cr(t.totalUsd-t.asRecordedUsd)})`),t.unpricedTurns&&r.push(`\u26A0 ${t.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(t.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(t.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(` ${ps(a,26)} ${ps(cr(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(` ${ps(a.name||a.id,30)} ${cr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ps(c.label,40)} ${ps(cr(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))}${ps(cr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
505
|
+
`)}_f();fc();le();Yd();import ID from"node:fs/promises";import RD from"node:path";qu();_r();var Il=e=>e.payload?.ts??e.ts,TD=12e4,lr=e=>Math.round(e*1e4)/1e4,AD=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function ED(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>Il(x)>=t,a=Ar([Js(n?.name),Js(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 E=Ar(x.payload.task);E&&i.set(x.boxId,E)}let c=new Map,l=x=>{let E=x||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},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,E)=>Il(x)-Il(E)),d=0,m=0,h=0,w={turns:0};for(let x of f){let E=l(x.boxId),_=E.segments[E.segments.length-1],q=Il(x);switch(E.lastTs!=null&&q>E.lastTs&&s(x)&&(_.activeMs+=Math.min(q-E.lastTs,TD)),E.lastTs=q,x.provider&&(E.provider=x.provider),x.type){case"turn.user":{let C=x.payload?.text;if(x.source==="hook"||!String(C||"").trim())break;let N=u(Ar(C));E.segments.push(N),s(x)&&(N.turns+=1);break}case"peer.message":{let C=x.payload?.text;if(!String(C||"").trim())break;let N=u(Ar(C));E.segments.push(N),s(x)&&(N.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let C=x.payload?.tool;C&&(_.tools[C]=(_.tools[C]||0)+1),s(x)&&(_.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:C,tokens:N,model:T,unpriced:W}=Qt(x);W&&(w.turns+=1),_.usd+=C,N&&(_.tokens.input+=N.input,_.tokens.output+=N.output,_.tokens.cacheRead+=N.cacheRead,_.tokens.cacheWrite+=N.cacheWrite),_.models[T]=(_.models[T]||0)+C,x.provider&&(_.provider=x.provider);break}case"handoff.done":s(x)&&(d+=1);break;case"subtask.merged":s(x)&&(m+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let y=new Map,b=!1;for(let[x,E]of c)for(let _ of E.segments){if(!(_.usd>0||_.activeMs>0||_.turns>0||_.toolCalls>0))continue;let C=_.textCat?.category??i.get(x)?.category??Xw(_.tools)?.category??a?.category??"uncategorized";y.has(C)||y.set(C,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let N=y.get(C);N.usd+=_.usd,N.activeMs+=_.activeMs,N.turns+=_.turns,N.toolCalls+=_.toolCalls;for(let[W,H]of Object.entries(_.models))N.models[W]=(N.models[W]||0)+H;let T=_.provider||E.provider;T&&(N.providers[T]=(N.providers[T]||0)+_.usd),T==="cursor"&&_.usd===0&&(b=!0)}return{perCategory:y,handoffs:d,merged:m,failed:h,sawCursorActivity:b,unpricedTurns:w.turns}}function dS(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let w of e||[]){let{track:y,events:b}=w;if(!b?.length){a+=1;continue}let{perCategory:x,handoffs:E,merged:_,failed:q,sawCursorActivity:C,unpricedTurns:N}=ED(w,{since:r});C&&(i=!0),c+=N;let T={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:_,subtasksFailed:q};for(let[W,H]of x){T.usd+=H.usd,T.activeMs+=H.activeMs,T.turns+=H.turns,T.categories[W]={usd:lr(H.usd),activeMs:H.activeMs};for(let[A,j]of Object.entries(H.models))T.models[A]=lr((T.models[A]||0)+j);for(let[A,j]of Object.entries(H.providers))T.providers[A]=lr((T.providers[A]||0)+j);o.has(W)||o.set(W,AD());let ae=o.get(W);ae.usd+=H.usd,ae.activeMs+=H.activeMs,ae.turns+=H.turns,ae.toolCalls+=H.toolCalls;for(let[A,j]of Object.entries(H.models))ae.models[A]=(ae.models[A]||0)+j;for(let[A,j]of Object.entries(H.providers))ae.providers[A]=(ae.providers[A]||0)+j;let J=ae.byTrack.get(y?.id)||{usd:0,activeMs:0};J.usd+=H.usd,J.activeMs+=H.activeMs,ae.byTrack.set(y?.id,J)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=lr(T.usd),T.dominantCategory=_D(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,m=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),h=[...o.entries()].map(([w,y])=>({category:w,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:lr(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:uS(y.models),providers:uS(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((b,x)=>x[1].usd-b[1].usd||x[1].activeMs-b[1].activeMs).slice(0,2).map(([b,x])=>({trackId:b,name:m.get(b),usd:lr(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:lr(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var _D=e=>{let t=null;for(let[n,r]of Object.entries(e))(!t||r.usd>t.v.usd||r.usd===t.v.usd&&r.activeMs>t.v.activeMs)&&(t={cat:n,v:r});return t?.cat??null},uS=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,lr(n)]));var ms={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function $D(e){let t=String(e||"7d");return t in ms?{key:t,ms:ms[t]}:{key:"7d",ms:ms["7d"]}}function OD(e,t){let n=new Set((t||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...t||[]];for(let o of e?.events||[]){let s=hc(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:e.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 CD(e){if(!e?.cwd)return null;try{let n=(await ID.readFile(RD.join(e.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 fS(e,{window:t="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}:$D(t),f=[];for(let d of e||[]){let m=await B(d),h=OD(d,m);i&&c<o&&(h=h.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:h,brief:await CD(d)})}return{window:l,...dS(f,{windowMs:u,now:c})}}le();_r();var ur=36e5,wS=24*ur,MD=400,ND=12,hs=e=>Math.round(e*1e4)/1e4;function PD({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),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(e||"7d")in ms?String(e||"7d"):"7d",i=ms[a];return{key:a,since:i==null?null:r-i,until:r}}var BD=(e,t)=>e!=null&&t-e<=48*ur?ur:wS;function pS(e,t){if(t===ur)return Math.floor(e/ur)*ur;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function DD(e,t){if(t===ur)return e+ur;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var mS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),hS=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},jD=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,hs(n)]));async function gS(e,t={}){let n=t.now??Date.now(),r=PD({...t,now:n}),o=r.since==null?wS:BD(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=mS(),f=0,d=0,m=0,h=0,w=0,y=new Set,b=new Map,x=[],E=[],_=[],q=null;for(let T of e||[]){let H=(await B(T)).filter(A=>A.type==="cost.recorded");if(!H.length){T.archivedAt||_.push(T.name||T.id);continue}let ae=[];for(let A of H){let j=A.ts??0;s(j)?ae.push(A):i(j)&&(w+=Qt(A).usd)}if(!ae.length)continue;let J=Un(ae,{boxLabels:Lm(T)});f+=J.totalUsd,d+=J.turns,m+=J.repricedTurns||0,h+=J.unpricedTurns||0;for(let A of J.unpricedModels||[])y.add(A);hS(u,J.totalTokens);for(let[A,j]of Object.entries(J.byModel))c[A]||={usd:0,events:0,tokens:mS()},c[A].usd=hs(c[A].usd+j.usd),c[A].events+=j.events,hS(c[A].tokens,j.tokens);for(let[A,j]of Object.entries(J.byProvider))l[A]||={usd:0,events:0},l[A].usd=hs(l[A].usd+j.usd),l[A].events+=j.events;for(let A of ae){let{usd:j}=Qt(A);if(!j)continue;let Q=pS(A.ts??0,o),fe=b.get(Q)??{t:Q,total:0,byProvider:{}};fe.total+=j;let Re=A.provider||"unknown";fe.byProvider[Re]=(fe.byProvider[Re]||0)+j,b.set(Q,fe),(q==null||Q<q)&&(q=Q)}x.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:J.totalUsd,turns:J.turns,providers:Object.keys(J.byProvider)});for(let[A,j]of Object.entries(J.byBox))E.push({trackId:T.id,trackName:T.name||T.id,boxId:A,...j})}let C=r.since!=null?pS(r.since,o):q,N=[];for(let T=C,W=0;T!=null&&T<r.until&&W<MD;T=DD(T,o),W++){let H=b.get(T);N.push(H?{t:T,total:hs(H.total),byProvider:jD(H.byProvider)}:{t:T,total:0,byProvider:{}})}return x.sort((T,W)=>W.usd-T.usd),E.sort((T,W)=>W.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:N,fleet:{totalUsd:hs(f),turns:d,repricedTurns:m,unpricedTurns:h,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:_},prior:a!=null?{totalUsd:hs(w),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:E.slice(0,ND)}}Ge();import ws from"node:fs/promises";import LD from"node:fs";import We from"node:path";import FD from"node:os";import{execFile as UD}from"node:child_process";import{promisify as HD}from"node:util";var WD=HD(UD),yS=process.env.BOSTRAT_STATE_DIR||We.join(process.env.HOME||FD.tmpdir(),".bostrat"),Rl=!1,Fm=null;async function Um(e,t){let n=await ws.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ws.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ws.mkdir(We.dirname(t),{recursive:!0}),await ws.copyFile(e,t),!0)}var qD=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 $l(e,t,{skip:n=null}={}){let r=0,o=await ws.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(qD.has(s.name)||n?.has(s.name))continue;let a=We.join(e,s.name);s.isDirectory()?r+=await $l(a,We.join(t,s.name)):s.isFile()&&(r+=await Um(a,We.join(t,s.name))?1:0)}return r}async function bS({stateDir:e=yS,dest:t=process.env.BOSTRAT_SYNC_DIR||`${yS}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ws.mkdir(t,{recursive:!0});let o=0;o+=await Um(We.join(e,"tracks.json"),We.join(t,"tracks.json"))?1:0,o+=await Um(We.join(e,"events.jsonl"),We.join(t,"events.jsonl"))?1:0,o+=await $l(We.join(e,"events"),We.join(t,"events")),o+=await $l(We.join(e,"memory"),We.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await $l(We.join(u.cwd,".bostrat"),We.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>WD("git",["-C",t,...u]);LD.existsSync(We.join(t,".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 nu(t).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||Rl))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Rl&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Rl=!1,Fm=null}catch(u){Rl=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
506
|
+
`).slice(-3).join(" | ").slice(0,300);f!==Fm&&(Fm=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)}}}up();vt();Sr();ze();Es();import{execFile as KD}from"node:child_process";import{promisify as GD}from"node:util";import{randomUUID as VD}from"node:crypto";import zD from"node:fs";import JD from"node:os";import YD from"node:path";var fr=GD(KD),Ol=e=>new Promise(t=>setTimeout(t,e)),pr=yu,Hm=process.env.BOSTRAT_USAGE_PROBE_CWD||YD.join(JD.homedir(),".bostrat","usage-probe"),XD=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,ZD=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,QD=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,kS=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,dr=null,ei=null,ti=null;async function e1(){try{return await fr(L,U("has-session","-t",pr)),!0}catch{return!1}}function t1(){dr&&clearTimeout(dr),dr=setTimeout(()=>n1().catch(()=>{}),XD),dr.unref&&dr.unref()}async function n1(){dr&&(clearTimeout(dr),dr=null);try{await fr(L,U("kill-session","-t",pr))}catch{}}async function r1(){let e=Date.now()+QD;for(;;){let t=await Wm().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await Ol(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Ol(500)}}async function o1(){if(await e1())return;if(!await Mn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");zD.mkdirSync(Hm,{recursive:!0}),await En(Hm);let e=VD();await fr(L,U("new-session","-d","-s",pr,"-c",Hm,"-x","200","-y","60",...gu()));let t=`${wu} --session-id ${e}`;await fr(L,U("send-keys","-t",pr,t,"Enter")),await r1()}async function Wm(){let{stdout:e}=await fr(L,U("capture-pane","-t",pr,"-p"));return e}async function s1(){await o1(),await fr(L,U("send-keys","-t",pr,"-l","/usage")),await fr(L,U("send-keys","-t",pr,"Enter"));let e=Date.now()+ZD,t="";for(;await Ol(400),t=await Wm(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await Ol(900),t=await Wm(),await fr(L,U("send-keys","-t",pr,"Escape")).catch(()=>{}),t1(),t}function a1(e){let t=String(e||"").split(`
|
|
507
|
+
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,t.length)&&!/^Current\s/i.test(t[c]);c++){let l=t[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=t[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=t.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return t.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 i1(e){let t=String(e||"").split(`
|
|
508
|
+
`).map(s=>s.replace(/\s+$/,"")),n=t.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<t.length&&!r.test(t[s]);s++)t[s].trim()&&o.push(t[s].trim());return o.length?o:null}async function xS({fresh:e=!1}={}){return!e&&ei&&Date.now()-ei.at<kS?ei.data:ti||(ti=(async()=>{try{let t=await s1(),n=a1(t),r={...n,contributors:i1(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return ei={at:Date.now(),data:r},r}catch(t){let n={session:null,week:null,contributors:null,subscription:!1,error:t?.message||String(t),capturedAt:Date.now()};return ei={at:Date.now()-(kS-3e3),data:n},n}finally{ti=null}})(),ti)}var c1=262144;function l1(e){let t=Number(e);if(!Number.isFinite(t)||t<0)return null;let n=Math.floor(t/3600),r=Math.round(t%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 u1=new Set([300]),d1=new Set([10080]);function f1(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:u1.has(t)?"session":d1.has(t)?"week":"other"}function p1(e){let t=Number(e);return!Number.isFinite(t)||t<=0?"Window":t%1440===0?`Window \xB7 ${t/1440}d`:t%60===0?`Window \xB7 ${t/60}h`:`Window \xB7 ${t}m`}function m1(e,t,n){if(!e||typeof e!="object")return null;let r=Number(e.used_percent);if(!Number.isFinite(r))return null;let o=null,s=Number(e.resets_at),a=Number(e.resets_in_seconds);return Number.isFinite(s)&&s>0?o=s*1e3:Number.isFinite(a)&&a>=0&&(o=(Number.isFinite(t)?t:n)+a*1e3),{pct:Math.round(r),resets:o!=null?l1((o-n)/1e3):null,resetsAt:o}}var ni=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function h1(e,t=Date.now()){let n=String(e||"").split(`
|
|
509
|
+
`),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 ni(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=ni(r);[c.primary,c.secondary].forEach((m,h)=>{let w=m1(m,l,t);if(!w)return;let y=f1(m.window_minutes);y===null&&(y=h===0?"session":"week"),y==="session"&&!u.session?u.session=w:y==="week"&&!u.week?u.week=w:(u.other??=[]).push({label:p1(m.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{...ni(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return ni(r)}return ni(r)}async function qm(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await xr(e?.cwd,e?.sessionId);if(!n)return{...t,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await et(n.file,c1),o=h1(r);return{...t,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{...t,session:null,week:null,subscription:!1,error:n?.message||String(n),capturedAt:Date.now()}}}async function vS(e,{fresh:t=!1}={}){let n=new Set;for(let o of e||[])for(let s of o.boxes||[])s.agent&&n.add(s.agent);let r=[];if((n.has("claude")||n.size===0)&&r.push(await Cl({agent:"claude"},{fresh:t})),n.has("codex")){let o=[],s=[...e||[]].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 qm(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await qm(null))}return n.has("cursor")&&r.push(await Cl({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Cl(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?qm(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await xS({fresh:t})}}hr();Ge();Ge();function w1(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}var g1=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Ml=(e,t)=>Object.assign(new Error(e),{status:t});async function SS(e,t){if(!w1(t))throw Ml("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${be(t)}${e}`,{headers:g1(t)})}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 TS(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await SS(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function AS(e,{env:t=process.env}={}){if(!e)throw Ml("message id required",400);return(await SS(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}ua();Ec();function Km(e){let t=e.split(".");if(t.length!==4)return null;let n=0;for(let r of t){let o=Number(r);if(!Number.isInteger(o)||o<0||o>255)return null;n=n*256+o}return n>>>0}function y1(e){let[t,n]=e.split("/"),r=Km(t);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 ES(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(y1).filter(Boolean)}function b1(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function _S(e){let t=ES(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:b1,allowed(r){if(n)return!0;let o=Km((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function IS(e,t){let n=ES(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Km(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import RS from"node:crypto";import Nl from"node:fs";import k1 from"node:path";function $S(e=process.env,{stateDir:t=Za,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=k1.join(t,"api-token");try{let a=Nl.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=RS.randomBytes(32).toString("base64url");try{Nl.mkdirSync(t,{recursive:!0}),Nl.writeFileSync(o,s+`
|
|
510
|
+
`,{mode:384}),Nl.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function ri(e){let t=e?.socket?.remoteAddress||"";if(!t)return!1;let n=t.replace(/^::ffff:/,"");return n==="::1"||n==="127.0.0.1"||/^127\./.test(n)}function OS(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:RS.timingSafeEqual(n,r)}function CS(e,t=null){let n=e?.headers?.["x-bostrat-token"];if(typeof n=="string"&&n.trim())return n.trim();let r=e?.headers?.authorization||"",o=/^Bearer\s+(.+)$/i.exec(r)?.[1];if(o)return o.trim();try{return(t??new URL(e.url,"http://node")).searchParams.get("token")||""}catch{return""}}var x1=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],v1=e=>x1.some(t=>t.test(e));function MS({token:e,enforce:t=!0,log:n=console}={}){return function(o,s,a){let i=o.path||o.url||"";if(!i.startsWith("/api"))return a();let c=CS(o);return OS(c,e)?a():v1(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"})):!t||ri(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 NS(e,t,{enforce:n=!0}={}){return!n||OS(CS(e),t)?!0:ri(e)}Yt();Uc();Uc();import S1 from"node:http";import T1 from"node:net";function Pl(e){if(!e)return null;let t=String(e).replace(/:\d+$/,"").toLowerCase();if(/^[\d.]+$/.test(t)||t.includes(":"))return null;let n=t.split(".");if(n.length<3)return null;let r=n[0];return/^[a-z0-9-]+$/.test(r)?r:null}function Bl(e){let t=Pl(e.headers.host);return t?{slug:t,view:Ff(t)}:null}var A1=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),E1=e=>{let t=A1(e.name);return`<!doctype html><meta charset="utf-8">
|
|
511
511
|
<meta http-equiv="refresh" content="3">
|
|
512
|
-
<title>${
|
|
512
|
+
<title>${t} \u2014 starting</title>
|
|
513
513
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
514
514
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
515
|
-
<p><b>${
|
|
515
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function PS(e,t,n){let r=Bl(e);if(!r)return n();let{view:o,slug:s}=r;if(!o)return t.statusCode=404,t.setHeader("content-type","text/plain"),t.end(`bostrat: no view named "${s}"`);if(o.state==="starting")return t.statusCode=503,t.setHeader("content-type","text/html"),t.end(E1(o));if(o.state!=="live"&&o.state!=="unhealthy")return t.statusCode=404,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...e.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":e.headers.host,"x-forwarded-proto":e.headers["x-forwarded-proto"]||"http"};delete a.authorization,delete a.cookie;let i=S1.request({host:"127.0.0.1",port:o.port,method:e.method,path:e.url,headers:a},c=>{t.writeHead(c.statusCode||502,c.headers),c.pipe(t)});i.on("error",()=>{t.headersSent?t.destroy():(t.statusCode=502,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),e.pipe(i),e.on("aborted",()=>i.destroy())}function BS(e,t,n){let r=Bl(e);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return t.write(`HTTP/1.1 404 Not Found\r
|
|
516
516
|
\r
|
|
517
517
|
bostrat: no live view "${s}"\r
|
|
518
|
-
`),
|
|
518
|
+
`),t.destroy(),!0;let a=T1.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${e.url} HTTP/1.1`];for(let[l,u]of Object.entries(e.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
|
|
519
519
|
`)+`\r
|
|
520
520
|
\r
|
|
521
|
-
`),n?.length&&a.write(n),
|
|
522
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,w1)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${g1}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=k1.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>OS)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${OS}-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>$S)throw new Error(`${r.length} bullets \u2014 over the ${$S}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function MS(t,{repos:e=jm()}={}){let n=new Set(e);return(await Cr()).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 NS(t,{md:e,day:n=null}){let r=x1(e),o=n==null||n===""?b1():String(n);if(!y1.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}}ma();Wn();Mo();st();ma();qt();Nr();Wn();Zs();Oc();ra();import{execFile as v1}from"node:child_process";import{promisify as S1}from"node:util";var Fm=S1(v1),PS=3e4,T1=30*6e4,A1=PS,E1=10*6e4,_1=10080*6e4,I1=60*6e4,R1=8,$1=1440*6e4,O1=3,BS="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Lm=60*6e4,C1=15*6e4,M1=9e4,N1=1440*6e4,$l=new Map,Ol=new Map,P1=/could not resolve|not found|no pull requests?/i,B1=t=>P1.test(String(t?.message||t||""));function D1(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Lm)}var DS={fast:A1,slow:E1,decay:I1};function jS(t,e){return t.armedAt&&e-t.armedAt<T1||D1(t,e)||LS(t,e,{grace:0})?"fast":e-(t.firstSeen||0)>_1?"decay":"slow"}function j1(t,e){return DS[jS(t,e)]}function Xa(t,e,n,r){let o=t.lanes?.get(J(e.repo,e.number))||{};return $c({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function L1(t,e){Ze("watch");try{let{stdout:n}=await Fm("gh",["pr","view",String(e),"--repo",t,"--json",BS],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Ie({slug:t})}});return JSON.parse(n||"null")}catch(n){throw Ln(n)&&Fn(n),n}}async function F1(t){Ze("watch");try{let{stdout:e}=await Fm("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",BS],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Ie({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw Ln(e)&&Fn(e),e}}async function U1(){let t=new Map;for(let e of await ot().catch(()=>[]))t.set(e.id,e);return t}async function H1(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 W1(t,e){let n=await Br(t);return n?jo(n,e):null}async function q1(t,e,n){let r=await Br(t);if(!r)throw new Error(`no local checkout for ${t}`);return Lo(r,e,{method:n})}async function K1(t,e){let n=await Br(t);if(!n)throw new Error(`no local checkout for ${t}`);return pa(n,e)}async function G1(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 us(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 V1=10*6e4;async function z1(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>$1){await us(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 us(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await us(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<V1)return;await us(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 us(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 us(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await FS(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function ds(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 J1(t,e){try{return await Fm("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ya(t,e,n,r=()=>!0){return(await B(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var fs=(t,e)=>({number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo});function LS(t,e,{grace:n=M1}={}){if(t.announced!==!1)return!1;let r=e-(t.firstSeen||0);return r>=n&&r<=N1}async function Y1(t,e,n,r){let o=r.now??Date.now();if(!LS(t,o))return!1;let s=n.get(t.trackId)||null;if(!s||s.archivedAt||s.transferredTo)return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??Mr)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await B(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(t.number))?(await(r.claim??Qe)(t.repo,t.number).catch(()=>null),!1):await(r.claim??Qe)(t.repo,t.number).catch(()=>null)?(await ds(s,"opened",{...fs(t,e),announcedBy:"watch"}).catch(()=>{}),console.log(`[pr-watch] announced passive link ${t.repo}#${t.number} \u2192 track ${s.id} (no active door claimed it)`),!0):!1}async function X1(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 pc(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await hc(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<C1;await Hb(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await Ub(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ya(a,"pushed",t.number,d=>d.payload?.sha===s)&&await ds(a,"pushed",{...fs(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Lm)await mc(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 mc(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Xa(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Ya(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await ds(a,"ci-fixed",{...fs(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await mc(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Lm)await wc(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await wc(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Xa(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Ya(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await ds(a,"reconciled",{...fs(t,e),base:i.baseRefName||null}).catch(()=>{}),await wc(t.repo,t.number).catch(()=>{})}}}}async function Z1(t,e,n,r){let o=Xa(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Ya(s,"closed",t.number)&&await ds(s,"closed",{...fs(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 Q1(t,e,n){Xa(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Ya(r,"lost",t.number)&&await ds(r,"lost",fs(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function FS(t,e,n,r,o={}){let s=Xa(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await B(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(t.number))||await r.emitMerged(a,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(t.repo,t.number).catch(()=>{}),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function tj(t={}){let{now:e=Date.now(),auth:n=Cf,view:r=L1,list:o=F1,links:s=qb,trackMap:a=U1,emitMerged:i=H1,mark:c=Hn,archiveIfArmed:l=tn,notifyIfArmed:u=en,notifyIfResolved:d=Xc,batchThreshold:f=O1,detail:p=W1,merge:h=q1,updateBranch:w=K1,lookup:y=Vt,disarm:k=ea,emitDisarmed:x=G1,localHasCommit:A=J1,claim:R=Qe,midTurn:V=Mr}=t;if(!(await n().catch(()=>null))?.authenticated||dc()>0)return 0;let T=(await s().catch(()=>[])).filter(j=>e-($l.get(J(j.repo,j.number))||0)>=j1(j,e));if(!T.length)return 0;let D=Date.now(),F=new Map,ct=new Map;for(let j of T){let at=jS(j,e);F.set(J(j.repo,j.number),{lane:at,intervalMs:DS[at]}),$l.set(J(j.repo,j.number),e),ct.has(j.repo)||ct.set(j.repo,[]),ct.get(j.repo).push(j)}let Q=await a().catch(()=>new Map),_={now:e,lanes:F,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,detail:p,merge:h,updateBranch:w,lookup:y,disarm:k,emitDisarmed:x,localHasCommit:A,claim:R,midTurn:V};for(let[j,at]of ct){let Et,Lt=new Set;if(at.length>f)Et=await o(j).catch(()=>null);else{Et=new Map;for(let it of at)try{let g=await r(j,it.number);g?.number!=null&&Et.set(Number(g.number),g)}catch(g){B1(g)&&Lt.add(J(it.repo,it.number))}}if(Et)for(let it of at){let g=J(it.repo,it.number),m=Et.get(Number(it.number));if(!m){if(Lt.has(g)){let S=(Ol.get(g)||0)+1;Ol.set(g,S),S>=R1&&(await Q1(it,Q,_),Ol.delete(g),$l.delete(g))}continue}Ol.delete(g),m.state==="MERGED"||m.state==="CLOSED"?(m.state==="MERGED"?await FS(it,m,Q,_):await Z1(it,m,Q,_),$l.delete(g)):(await Y1(it,m,Q,_).catch(()=>{}),await X1(it,m,Q,_).catch(()=>{}),it.autoMerge&&await z1(it,Q,_).catch(()=>{}))}}let U={};for(let{lane:j}of F.values())U[j]=(U[j]||0)+1;return console.log(`[pr-watch] probed ${T.length} link(s) (${Object.entries(U).map(([j,at])=>`${j} ${at}`).join(", ")}) in ${Date.now()-D}ms`),T.length}function US(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await tj()}catch{}finally{t=!1}}},PS)}Gt();st();Nr();import{randomUUID as ej}from"node:crypto";var nj=2e4,HS=3,ps=new Map,Um=new Set;async function WS(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 rj(t={}){let{sessions:e=null,tracks:n=null,append:r=I,read:o=B,now:s=Date.now()}=t;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=e??await be(),i=n??await ot()}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 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=(ps.get(d.name)||0)+1;ps.set(d.name,p),p===HS&&(Um.add(d.name),await WS(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:ej(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(ps.get(d.name)||0)>=HS;if(ps.delete(d.name),p||!Um.has(d.name)){Um.add(d.name);let h=await WS(u,d.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ps.keys()])l.has(u)||ps.delete(u)}var Za=null,Hm=!1;function qS(){return Za?()=>{}:(Za=setInterval(()=>{Hm||(Hm=!0,rj().catch(()=>{}).finally(()=>{Hm=!1}))},nj),Za.unref?.(),()=>{clearInterval(Za),Za=null})}Ro();Lf();st();ma();Mo();Ro();import{randomUUID as oj}from"node:crypto";async function sj(){let t=await Promise.resolve().then(()=>(Nr(),V0));return{sayToLead:t.sayToLead,queueShipCompleteNotify:t.queueShipCompleteNotify}}var Wm={listRepoPrs:Pr,readEvents:B,appendEvent:I,sayDeps:sj,currentBranch:Oo};async function aj(t,e=Wm,{branch:n=null}={}){if(!t?.cwd)return null;let r=n||await(e.currentBranch??Oo)(t.workspace?.kind==="multi"?null:t.cwd).catch(()=>null)||t?.workspace?.branch;if(!r)return null;let o;try{o=await e.listRepoPrs(t.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function qm(t,e,{actions:n,notify:r=!1}={},o=Wm){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 aj(t,o,{branch:s.branch});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=oj();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 Km(t,{offerId:e,choice:n}={},r=Wm){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=cc.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(t.id,Io(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,vb(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 yn from"node:fs/promises";import ms from"node:path";import XS from"node:os";import KS from"node:fs/promises";import Cl from"node:path";import{execFile as ij}from"node:child_process";import{promisify as cj}from"node:util";import{fileURLToPath as lj}from"node:url";var uj=cj(ij),VS=Cl.dirname(lj(import.meta.url)),dj=Cl.resolve(VS,"..","..","client","dist"),fj="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",pj=new Map([...fj].map((t,e)=>[t,e]));function mj(t){let e=[],n=0,r=0;for(let o of t){let s=pj.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 hj(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=mj(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 wj(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 GS=new Map;async function gj(t){let e;try{e=await KS.stat(t)}catch{return null}let n=GS.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await KS.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:hj(r.mappings||"")};return GS.set(t,o),o}catch{return null}}var Gm=null;function yj(){return Gm||(Gm=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):uj("git",["rev-parse","--short","HEAD"],{cwd:Cl.resolve(VS,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Gm}var bj=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function kj(t,{distDir:e=dj}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(bj)){let[a,i,c,l]=s,u=await gj(Cl.join(e,"assets",`${i}.map`)),d=u?wj(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 zS(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await yj();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await kj(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var xj=()=>process.env.BOSTRAT_STATE_DIR||ms.join(process.env.HOME||XS.tmpdir(),".bostrat"),ir=()=>ms.join(xj(),"telemetry"),ZS=()=>ms.join(ir(),".relay-offsets.json"),vj=50,Sj=14,Tj=3e4,JS=500,ce=(t,e)=>typeof t=="string"?t.slice(0,e):"";function Aj(t,e){if(!t||typeof t!="object")return null;let n=ce(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:ce(t.anonId,64),sessionId:ce(t.sessionId,64),userId:ce(t.userId,64),release:ce(t.release,64),appVersion:ce(t.appVersion,32),route:ce(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:ce(t.error.kind,256),message:ce(t.error.message,8192),stack:ce(t.error.stack,32768),fingerprint:ce(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:ce(s?.n,64),r:ce(s?.r,128)}))),o}var QS=t=>new Date(t).toISOString().slice(0,10),YS=0;async function tT(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,vj):[]).map(s=>Aj(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await zS(s);await yn.mkdir(ir(),{recursive:!0});let o=ms.join(ir(),`${QS(e)}.ndjson`);return await yn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
523
|
-
`).join(""),"utf8"),
|
|
524
|
-
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=JS){let p=d.slice(f,f+JS).map(w=>_j(w)),h=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(!h||!h.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await rT(r)}return{sent:o,disabled:!1}}var Vm=null,zm=!1;function oT(){Vm||(Vm=setInterval(async()=>{if(!zm){zm=!0;try{await Ij()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{zm=!1}}},Tj),Vm.unref?.())}import Pl from"node:fs/promises";import lT from"node:path";import Rj from"node:os";var uT=()=>process.env.BOSTRAT_STATE_DIR||lT.join(process.env.HOME||Rj.tmpdir(),".bostrat"),dT=()=>lT.join(uT(),"dictation-glossary.json"),$j=100,Oj=500,Cj=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Bl=null,Jm=null,Ym=Promise.resolve();function sT(){let t={},e=Date.now();for(let n of Cj)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function fT(){return Bl||(Jm??=Pl.readFile(dT(),"utf8").then(t=>JSON.parse(t)?.terms??sT()).catch(()=>sT()).then(t=>Bl=t).finally(()=>Jm=null),Jm)}async function aT(){let t=JSON.stringify({terms:Bl},null,2),e=dT(),n=`${e}.${process.pid}.tmp`;await Pl.mkdir(uT(),{recursive:!0}),await Pl.writeFile(n,t),await Pl.rename(n,e)}function Mj(){return Ym=Ym.then(aT,aT),Ym.catch(()=>{})}async function Xm(){return Object.values(await fT()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var Nj=t=>Math.max(1,Math.ceil(t.length/4));async function Pj(){let t=[],e=0;for(let{term:n}of await Xm()){let r=Nj(n);if(!(e+r>Oj)&&(t.push(n),e+=r,t.length>=$j))break}return t}async function Bj(t,e){let n=await fT(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var iT=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Nl=t=>t.toLowerCase();function cT(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function pT(t,e){let n=iT(t),r=iT(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]=Nl(n[u])===Nl(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;)Nl(n[c])===Nl(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(cT(r[l])&&i.add(r[l]),l++);for(;l<s;l++)cT(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await Bj(u,"learned");return await Mj(),[...i]}var Qa=null;function Dj(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 mT(){if(!Qa)return null;let t=await Da();if(!t||t.apiKey.slice(-6)!==Qa.keyTail)return null;let{keyTail:e,...n}=Qa;return n}async function hT(t,e){let n=await Da();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 Pj())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=Dj(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??""}}st();var Zm={"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 jj(t){if(!t)return null;let e=String(t).trim();if(Zm[e])return e;let n=null;for(let r of Object.keys(Zm))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function Lj(t,e={}){let n=Zm[jj(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 Fj(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 ti=null;async function Uj(){if(!ti)return null;let t=await ul();if(!t||t.apiKey.slice(-6)!==ti.keyTail)return null;let{keyTail:e,...n}=ti;return n}async function Hj(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=Fj(e.status,o);s.persistent&&(ti={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}ti=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 Wj={openai:Hj};async function wT(){let t=await ul();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await Uj()}:{configured:!1}}async function gT(){let t=await ul();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 Wj[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function yT(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=Lj(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 TT=dt.dirname(Kj(import.meta.url)),bT=Number(process.env.PORT)||3362,Dl=Im(process.env),AT=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,hs=pS(process.env.BOSTRAT_ALLOW_IPS),rh=mS(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Rl),ET=wS(),_T=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",v=ge();v.disable("x-powered-by");v.use((t,e,n)=>{e.setHeader("x-content-type-options","nosniff"),Il(t.headers.host)||(e.setHeader("x-frame-options","DENY"),e.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});v.use((t,e,n)=>{if(Ja(t)||hs.allowed(hs.clientIp(t))||rh.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});v.use(xS);v.use(bS({token:ET,enforce:_T}));v.use("/api/mocks",ge.json({limit:"1mb"}));v.use("/api/mocks",ge.raw({type:"image/png",limit:"24mb"}));v.use("/api/mocks",ge.raw({type:["video/mp4","video/webm"],limit:"105mb"}));v.use("/api/runbooks",ge.json({limit:"1mb"}));v.use("/api/tracks/adopt",ge.json({limit:"1mb"}));v.use("/api/telemetry",ge.json({limit:"256kb"}));v.use("/api/hook",ge.json({limit:"2mb"}));v.use("/api/dictation",ge.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));v.use("/api/tracks/:id/upload",ge.raw({type:()=>!0,limit:"25mb"}));v.use(ge.json({limit:"64kb"}));v.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:re,hostTmuxAlive:await _n(),autoTriggerPct:AT})});v.get("/api/sessions",async(t,e)=>{e.json({sessions:await be()})});v.post("/api/sessions",async(t,e)=>{let{displayName:n,cwd:r}=t.body||{};if(typeof n!="string")return e.status(400).json({error:"displayName (string) required"});try{let o=await In(n,r||void 0);e.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;e.status(a).json({error:s})}});v.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 nt(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}`})}});v.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 nt(n);if(!r)return e.status(404).json({error:"session not found"});let o=await Cs(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});v.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 Hg(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)})}});v.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=Vi(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 kT=10;v.get("/api/idle-guard",async(t,e)=>{try{e.json(z0({tracks:await ot(),views:await Ef(),queuedOf:n=>Jo(n)}))}catch{e.status(500).json({busy:!0,reasons:["error"]})}});v.get("/api/tracks",async(t,e)=>{let n=(await ot()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>kT){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,kT).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:$a,agentAvailability:await Kc(),tickets:await gv()})});v.get("/api/cost",async(t,e)=>{try{let n=await ot();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await zv(n);if(r.cursorUsage=pf(),t.query.format==="text")return e.type("text/plain").send(Jv(r));e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/roundup",async(t,e)=>{try{e.json(await Zv(await ot(),{window:t.query.window,from:t.query.from,to:t.query.to}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/spend",async(t,e)=>{try{let n=await rS(await ot(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=pf(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=Ys(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.get("/api/usage/caps",(t,e)=>{e.json(Ys())});v.post("/api/usage/caps",async(t,e)=>{try{e.json(await rc(t.body?.caps??{},{source:"local"}))}catch(n){e.status(400).json({error:n?.message||String(n)})}});v.get("/api/usage/limits",async(t,e)=>{try{let n=t.query.fresh==="1"||t.query.fresh==="true";e.json(await cS(await ot(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.post("/api/hook",async(t,e)=>{try{let{mode:n,target:r}=await ih(t.get(si),{byToken:vp,legacy:()=>sa(t.body?.session_id,t.body?.cwd)});if(n==="refused")return gs("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return gs("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&&gs("mismatch",{track:r.track.id,box:r.box.name}),gs(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 Lv(t.body||{},()=>r);e.json({ok:!0,events:o.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});v.post("/api/telemetry",async(t,e)=>{try{if(!eT(hs.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await tT(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});v.get("/api/repos",async(t,e)=>{try{let n=await qe(),r=new Set((await Promise.all(n.map(a=>ut(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await vs()).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)})}});v.get("/api/github/token",async(t,e)=>{try{e.json(await di(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});v.get("/api/github/status",(t,e)=>{e.json({configured:ui(),calls:Nb(),latency:Vk()})});v.post("/api/github/refresh",(t,e)=>{gh(),e.json({ok:!0})});v.get("/api/gmail/search",async(t,e)=>{try{let n=await uS(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)})}});v.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 oh(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 jl({repo:t=null,number:e=null,branch:n=null}){let r=t?await ut(t):null;if(r&&e!=null){let s=await Vt(r,e),a=s&&await X(s.trackId);if(a)return a}if(!n)return null;let o=await ot();return yc(o,n,s=>oh(s,t,r))}async function IT({repo:t=null,number:e=null,branch:n=null}){let r=await jl({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=t?await ut(t).catch(()=>null):null,s=await ot();return yc(s,n,a=>!a.archivedAt&&!a.transferredTo&&oh(a,t,o))}async function Ee(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await jl({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await ut(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 Jj(t){let e=(await ot()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of t??[]){if(!n.prs?.length)continue;let r=await ut(n.path);if(r)for(let o of n.prs){if(await Vt(r,o.number))continue;let s=await yc(e,o.headRefName,a=>oh(a,n.path,r));s&&await se({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}v.get("/api/git/prs",async(t,e)=>{try{let n=await tx();await Jj(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.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(!fa.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 ut((await sn(n)).path);if(!k)throw new Error("repo has no GitHub remote");let x=await jl({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 pa(n,y),await se({repo:k,number:y,trackId:x.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Qs(k,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Or(k,y,i===!0),await ta(k,y,c===!0&&i!==!0),Be(),await Ee("auto-armed",{number:y,url:d,title:u,branch:l,repo:n,track:x,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:y,auto:!0})}let f=await Lo(n,r,{method:o||"squash"});await Ee("merged",{number:f.number,url:d,title:u,branch:l,repo:n});let p=await ut(n).catch(()=>null),h=!1;if(p&&(i===!0&&await Or(p,f.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ta(p,f.number,!0).catch(()=>{}),await en(p,f.number).catch(()=>{}),h=await tn(p,f.number).catch(()=>!1),await Hn(p,f.number,"merged").catch(()=>{})),i===!0&&!h){let w=await jl({repo:n,number:f.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Hr(w.id).catch(()=>{})}e.json(f)}catch(f){let p=f?.message||String(f),h=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;e.status(h).json({error:p})}});v.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 ut((await sn(n)).path).catch(()=>null),l=c?await ea(c,i).catch(()=>null):null;try{await Pf(n,i)}catch(u){if(!l)throw u}Be(),await Ee("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})}});v.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await ut(n).catch(()=>null),s=await ox(n,{method:r||"merge",onMerged:a=>{Ee("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&en(o,a.number).catch(()=>{}).then(()=>tn(o,a.number)).catch(()=>{}).then(()=>Hn(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})}});v.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:rx()})});v.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(!fa.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return e.status(400).json({error:"trackId required"});let c=await X(n);if(!c)return e.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Cr()).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 Or(f.repo,f.number,o===!0).catch(()=>{}),await ta(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 h=await Br(f.repo);if(!h)throw new Error(`no local checkout for ${f.repo}`);let w=await jo(h,f.number);d.push({r:p,l:f,repoPath:h,pr:w,plan:ex(w,{update:r!==!1})})}catch(h){p.ok=!1,p.action="failed",p.reason=h?.message||String(h)}}for(let{r:f,l:p,repoPath:h,pr:w,plan:y}of nx(d))try{if(y.action==="skip")f.action="skipped",f.reason=y.reason;else if(y.action==="merge")await Lo(h,p.number,{method:i}),await Ee("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c}),await en(p.repo,p.number).catch(()=>{}),await tn(p.repo,p.number).catch(()=>{}),await Hn(p.repo,p.number,"merged").catch(()=>{}),f.action="merged";else{if(y.action==="update-arm"&&(await pa(h,p.number).catch(()=>{}),f.action="updated-armed"),!await Qs(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await Ee("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c,...f.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(k){f.ok=!1,f.action="failed",f.reason=k?.message||String(k)}if(Be(),!u.some(f=>f.ok)){if(o===!0)for(let f of l)await Or(f.repo,f.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let f of l)await ta(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})}});v.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 X(n);if(!r)return e.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Cr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ea(a.repo,a.number).catch(()=>null),c=await Br(a.repo).catch(()=>null);c&&await Pf(c,a.number).catch(()=>{}),i&&(await Ee("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}))}Be(),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})}});v.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 X(n);if(!i)return e.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=ie(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 No(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 ut(w.dir).catch(()=>null);k&&y.number&&await se({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Ee("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 h=p.filter(w=>w.ok);return h.length?e.json({ok:!0,multi:!0,results:p,url:h[0].url,number:h[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 ut(i.cwd).catch(()=>null);l&&c.number&&await se({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Ee("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})}});v.get("/api/tracks/:id/repos",async(t,e)=>{try{let n=await X(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await No(n),o=new Map(r.map(a=>[a.base,a])),s=ie(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)})}});v.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 Cp(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})}});v.delete("/api/tracks/:id/repos/:name",async(t,e)=>{try{let n=await Mp(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})}});v.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await jo(n,r),s=await ut(n).catch(()=>null),a=await IT({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=ie(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await sx(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?dt.join("ci",i.name):"ci",d=dt.join(a.cwd,".bostrat",u),f=dt.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ht.promises.mkdir(d,{recursive:!0}),await Ht.promises.writeFile(dt.join(a.cwd,f),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
525
|
-
${
|
|
521
|
+
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function DS(e,t){let n=typeof t=="string"?{host:t}:t||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${e.slug}.${r.replace(/:\d+$/,"")}/`:`http://${e.slug}.${_1(n.host)}/`}function _1(e){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let t=String(e||"").replace(/:\d+$/,"").toLowerCase();if(!t||!t.includes(".")||/^[\d.]+$/.test(t))return"bostrat.globopi";let n=t.split(".");return Pl(t)&&Ff(n[0])?n.slice(1).join("."):t}Yo();Qf();zc();kf();le();import{randomUUID as I1}from"node:crypto";var jS=32768,LS=120,FS=1e3,US=new Set(["claude","codex","cursor"]),R1=new Set(["off","transition","nag"]),$1=[[0,59],[0,23],[1,31],[1,12],[0,6]];function O1(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=$1[r];return String(n).split(",").every(a=>{if(!a)return!1;let[i,c]=a.split("/");if(c!==void 0&&!(Number.isInteger(Number(c))&&Number(c)>=1))return!1;if(i==="*")return!0;let[l,u]=i.includes("-")?i.split("-").map(Number):[Number(i),Number(i)];return Number.isInteger(l)&&Number.isInteger(u)&&l>=o&&u<=s&&l<=u})})}var vn=e=>String(e??"").trim();function C1(e,t){let n=e&&typeof e=="object"?e:{},r=vn(n.name);if(!r)throw new Error("a loop plan needs a name");if(r.length>LS)throw new Error(`name is too long (${r.length}); cap is ${LS}`);let o=vn(n.schedule);if(!o)throw new Error('a loop plan needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!O1(o))throw new Error(`invalid cron schedule: "${o}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);let s=vn(n.prompt);if(!s)throw new Error("a loop plan needs a prompt \u2014 it is what the run does");if(s.length>jS)throw new Error(`prompt is too long (${s.length} chars); cap is ${jS}`);let a=vn(n.expectation)||null;if(a&&a.length>FS)throw new Error(`expectation is too long (${a.length}); cap is ${FS}`);let i=vn(n.notify)||"off";if(!R1.has(i))throw new Error(`notify must be one of off | transition | nag, got "${n.notify}"`);let c=n.assignment??null,l=vn(typeof c=="string"?c:c?.provider)||"claude";if(!US.has(l))throw new Error(`unknown provider "${l}" \u2014 one of ${[...US].join(", ")}`);let u=typeof c=="object"&&c&&vn(c.model)||null,f=vn(t?.workspace?.repo)||vn(t?.workspace?.base)||null;if(!f)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:u?{provider:l,model:u}:{provider:l},repo:f}}async function HS(e,t){let n=C1(t,e),r=I1();return await I({id:e.id,cwd:e.cwd},{type:"loop.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:r,...n}}),{proposalId:r,...n}}async function WS(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await B({id:e.id,cwd:e.cwd}),a=s.find(c=>c.type==="loop.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="loop.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await I({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("loopId required \u2014 the loop is created on the plane before the card is answered");let i=a.payload;return await I({id:e.id,cwd:e.cwd},{type:"loop.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:String(r),name:i.name,schedule:i.schedule,provider:i.assignment?.provider??"claude",repo:i.repo??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await I({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",loopId:String(r)}}),{proposalId:t,action:"created",loopId:String(r)}}On();le();Vn();zc();var qS=10,KS=200,M1=120,N1=24,P1=/^\d{4}-\d{2}-\d{2}$/;function Gm(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var B1=(e=Date.now())=>new Date(e).toISOString().slice(0,10);function GS(e,t=Date.now()){if(!e)return t-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(e).trim());if(n)return t-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(e);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${e}" \u2014 use 24h, 7d, or an ISO date`)}var D1=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function j1(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Ea(e);if(t.length)throw new Error(`changelog entry appears to contain a secret VALUE (${t.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of e.split(`
|
|
522
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,M1)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${N1}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=D1.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>KS)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${KS}-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>qS)throw new Error(`${r.length} bullets \u2014 over the ${qS}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function VS(e,{repos:t=Gm()}={}){let n=new Set(t);return(await Dr()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&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 zS(e,{md:t,day:n=null}){let r=j1(t),o=n==null||n===""?B1():String(n);if(!P1.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await I({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.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}}Ho();Vn();Ko();le();Ho();Ge();Ur();Vn();ua();Ec();ha();import{execFile as L1}from"node:child_process";import{promisify as F1}from"node:util";var zm=F1(L1),JS=3e4,U1=30*6e4,H1=JS,W1=10*6e4,q1=10080*6e4,K1=60*6e4,G1=8,V1=1440*6e4,z1=3,YS="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Vm=60*6e4,J1=15*6e4,Y1=9e4,X1=1440*6e4,Dl=new Map,jl=new Map,Z1=/could not resolve|not found|no pull requests?/i,Q1=e=>Z1.test(String(e?.message||e||""));function ej(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Vm)}var XS={fast:H1,slow:W1,decay:K1};function ZS(e,t){return e.armedAt&&t-e.armedAt<U1||ej(e,t)||QS(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>q1?"decay":"slow"}function tj(e,t){return XS[ZS(e,t)]}function si(e,t,n,r){let o=e.lanes?.get(X(t.repo,t.number))||{};return Ac({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function nj(e,t){rn("watch");try{let{stdout:n}=await zm("gh",["pr","view",String(t),"--repo",e,"--json",YS],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await $t({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Jn(n)&&Yn(n),n}}async function rj(e){rn("watch");try{let{stdout:t}=await zm("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",YS],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await $t({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Jn(t)&&Yn(t),t}}async function oj(){let e=new Map;for(let t of await ce().catch(()=>[]))e.set(t.id,t);return e}async function sj(e,t){await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function aj(e,t){let n=await Lr(e);return n?Fo(n,t):null}async function ij(e,t,n){let r=await Lr(e);if(!r)throw new Error(`no local checkout for ${e}`);return Uo(r,t,{method:n})}async function cj(e,t){let n=await Lr(e);if(!n)throw new Error(`no local checkout for ${e}`);return pa(n,t)}async function lj(e,t){await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function gs(e,t,n,r,o){if(!await r.disarm(e.repo,e.number).catch(()=>null))return;let a=n.get(e.trackId);a&&await r.emitDisarmed(a,{number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo,reason:o}).catch(()=>{})}var uj=10*6e4;async function dj(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>V1){await gs(e,null,t,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(e.repo,e.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await gs(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await gs(e,o,t,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(e.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<uj)return;await gs(e,o,t,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(e.repo,e.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(e.repo,e.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await gs(e,o,t,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(e.repo,e.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await gs(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await eT(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function ys(e,t,n){await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function fj(e,t){try{return await zm("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function oi(e,t,n,r=()=>!0){return(await B(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var bs=(e,t)=>({number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo});function QS(e,t,{grace:n=Y1}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=X1}async function pj(e,t,n,r){let o=r.now??Date.now();if(!QS(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||s.archivedAt||s.transferredTo)return!1;let a=(s.boxes||[]).find(f=>f.name===s.leadBox);return await(r.midTurn??Fr)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await B(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??tn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??tn)(e.repo,e.number).catch(()=>null)?(await ys(s,"opened",{...bs(e,t),announcedBy:"watch"}).catch(()=>{}),console.log(`[pr-watch] announced passive link ${e.repo}#${e.number} \u2192 track ${s.id} (no active door claimed it)`),!0):!1}async function mj(e,t,n,r){let o=r.now??Date.now(),s=t.headRefOid||null,a=n.get(e.trackId)||null;if(s&&(e.ciFix?.headSha&&s!==e.ciFix.headSha&&await gc(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await bc(e.repo,e.number,{headSha:s}).catch(()=>{})),s&&s!==e.lastHeadSha){let i=!e.lastHeadSha,c=!!(e.ciFix||e.reconcile||e.ciFixAwait||e.reconcileAwait),l=!!e.baseFoldAt&&o-e.baseFoldAt<J1;await Wb(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Hb(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await oi(a,"pushed",e.number,f=>f.payload?.sha===s)&&await ys(a,"pushed",{...bs(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Vm)await yc(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await yc(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=si(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await oi(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await ys(a,"ci-fixed",{...bs(e,t),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await yc(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Vm)await kc(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await kc(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){si(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await oi(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await ys(a,"reconciled",{...bs(e,t),base:i.baseRefName||null}).catch(()=>{}),await kc(e.repo,e.number).catch(()=>{})}}}}async function hj(e,t,n,r){let o=si(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await oi(s,"closed",e.number)&&await ys(s,"closed",{...bs(e,t),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(e.repo,e.number,"closed").catch(()=>{}),await r.mark(e.repo,e.number,"closed").catch(()=>{})}async function wj(e,t,n){si(n,e,"lost",null);let r=t.get(e.trackId);r&&!await oi(r,"lost",e.number)&&await ys(r,"lost",bs(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function eT(e,t,n,r,o={}){let s=si(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await B(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function gj(e={}){let{now:t=Date.now(),auth:n=sf,view:r=nj,list:o=rj,links:s=Kb,trackMap:a=oj,emitMerged:i=sj,mark:c=Gn,archiveIfArmed:l=sn,notifyIfArmed:u=an,notifyIfResolved:f=sl,batchThreshold:d=z1,detail:m=aj,merge:h=ij,updateBranch:w=cj,lookup:y=Je,disarm:b=la,emitDisarmed:x=lj,localHasCommit:E=fj,claim:_=tn,midTurn:q=Fr}=e;if(!(await n().catch(()=>null))?.authenticated||Sc()>0)return 0;let T=(await s().catch(()=>[])).filter(Q=>t-(Dl.get(X(Q.repo,Q.number))||0)>=tj(Q,t));if(!T.length)return 0;let W=Date.now(),H=new Map,ae=new Map;for(let Q of T){let fe=ZS(Q,t);H.set(X(Q.repo,Q.number),{lane:fe,intervalMs:XS[fe]}),Dl.set(X(Q.repo,Q.number),t),ae.has(Q.repo)||ae.set(Q.repo,[]),ae.get(Q.repo).push(Q)}let J=await a().catch(()=>new Map),A={now:t,lanes:H,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:m,merge:h,updateBranch:w,lookup:y,disarm:b,emitDisarmed:x,localHasCommit:E,claim:_,midTurn:q};for(let[Q,fe]of ae){let Re,ue=new Set;if(fe.length>d)Re=await o(Q).catch(()=>null);else{Re=new Map;for(let ie of fe)try{let g=await r(Q,ie.number);g?.number!=null&&Re.set(Number(g.number),g)}catch(g){Q1(g)&&ue.add(X(ie.repo,ie.number))}}if(Re)for(let ie of fe){let g=X(ie.repo,ie.number),p=Re.get(Number(ie.number));if(!p){if(ue.has(g)){let S=(jl.get(g)||0)+1;jl.set(g,S),S>=G1&&(await wj(ie,J,A),jl.delete(g),Dl.delete(g))}continue}jl.delete(g),p.state==="MERGED"||p.state==="CLOSED"?(p.state==="MERGED"?await eT(ie,p,J,A):await hj(ie,p,J,A),Dl.delete(g)):(await pj(ie,p,J,A).catch(()=>{}),await mj(ie,p,J,A).catch(()=>{}),ie.autoMerge&&await dj(ie,J,A).catch(()=>{}))}}let j={};for(let{lane:Q}of H.values())j[Q]=(j[Q]||0)+1;return console.log(`[pr-watch] probed ${T.length} link(s) (${Object.entries(j).map(([Q,fe])=>`${Q} ${fe}`).join(", ")}) in ${Date.now()-W}ms`),T.length}function tT(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await gj()}catch{}finally{e=!1}}},JS)}ze();le();Ur();import{randomUUID as yj}from"node:crypto";var bj=2e4,nT=3,ks=new Map,Jm=new Set;async function rT(e,t,n){let r=await n(e).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===t?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function kj(e={}){let{sessions:t=null,tracks:n=null,append:r=I,read:o=B,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await xt(),i=n??await ce()}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(m=>m.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 m=(ks.get(f.name)||0)+1;ks.set(f.name,m),m===nT&&(Jm.add(f.name),await rT(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:yj(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let m=(ks.get(f.name)||0)>=nT;if(ks.delete(f.name),m||!Jm.has(f.name)){Jm.add(f.name);let h=await rT(u,f.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ks.keys()])l.has(u)||ks.delete(u)}var ai=null,Ym=!1;function oT(){return ai?()=>{}:(ai=setInterval(()=>{Ym||(Ym=!0,kj().catch(()=>{}).finally(()=>{Ym=!1}))},bj),ai.unref?.(),()=>{clearInterval(ai),ai=null})}Bo();Wf();le();Ho();Ko();Bo();import{randomUUID as xj}from"node:crypto";async function vj(){let e=await Promise.resolve().then(()=>(Ur(),sv));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Xm={listRepoPrs:Nt,readEvents:B,appendEvent:I,sayDeps:vj,currentBranch:Wo};async function Sj(e,t=Xm,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Wo)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function Zm(e,t,{actions:n,notify:r=!1}={},o=Xm){let s=t?.payload||{};if(!Array.isArray(n)||!n.includes("pr")||s.toPr||Array.isArray(s.repos)&&s.repos.length||!s.branch)return null;let a=await Sj(e,o,{branch:s.branch});if(!a)return null;let i={id:e.id,cwd:e.cwd};await o.appendEvent(i,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:s.offerId,action:"pr-exists",actions:n,...r?{notify:!0}:{},existingPr:{number:a.number}}});let c=xj();return await o.appendEvent(i,{type:"pr.exists.offered",actor:"system",source:"orchestrator",boxId:t.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 Qm(e,{offerId:t,choice:n}={},r=Xm){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))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=No.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Po(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(e.id,Cb(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:e.id,cwd:e.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:t,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}import Sn from"node:fs/promises";import xs from"node:path";import dT from"node:os";import sT from"node:fs/promises";import Ll from"node:path";import{execFile as Tj}from"node:child_process";import{promisify as Aj}from"node:util";import{fileURLToPath as Ej}from"node:url";var _j=Aj(Tj),iT=Ll.dirname(Ej(import.meta.url)),Ij=Ll.resolve(iT,"..","..","client","dist"),Rj="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",$j=new Map([...Rj].map((e,t)=>[e,t]));function Oj(e){let t=[],n=0,r=0;for(let o of e){let s=$j.get(o);if(s===void 0)return t;n+=(s&31)<<r,s&32?r+=5:(t.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return t}function Cj(e){let t=[],n=0,r=0,o=0;for(let s of e.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=Oj(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])))}t.push(a)}return t}function Mj(e,t){if(!e||e.length===0||e[0][0]>t)return null;let n=0,r=e.length-1;for(;n<r;){let o=n+r+1>>1;e[o][0]<=t?n=o:r=o-1}return e[n]}var aT=new Map;async function Nj(e){let t;try{t=await sT.stat(e)}catch{return null}let n=aT.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await sT.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:Cj(r.mappings||"")};return aT.set(e,o),o}catch{return null}}var eh=null;function Pj(){return eh||(eh=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):_j("git",["rev-parse","--short","HEAD"],{cwd:Ll.resolve(iT,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),eh}var Bj=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function Dj(e,{distDir:t=Ij}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(Bj)){let[a,i,c,l]=s,u=await Nj(Ll.join(t,"assets",`${i}.map`)),f=u?Mj(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.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(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function cT(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await Pj();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await Dj(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var jj=()=>process.env.BOSTRAT_STATE_DIR||xs.join(process.env.HOME||dT.tmpdir(),".bostrat"),mr=()=>xs.join(jj(),"telemetry"),fT=()=>xs.join(mr(),".relay-offsets.json"),Lj=50,Fj=14,Uj=3e4,lT=500,ut=(e,t)=>typeof e=="string"?e.slice(0,t):"";function Hj(e,t){if(!e||typeof e!="object")return null;let n=ut(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:ut(e.anonId,64),sessionId:ut(e.sessionId,64),userId:ut(e.userId,64),release:ut(e.release,64),appVersion:ut(e.appVersion,32),route:ut(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:ut(e.error.kind,256),message:ut(e.error.message,8192),stack:ut(e.error.stack,32768),fingerprint:ut(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:ut(s?.n,64),r:ut(s?.r,128)}))),o}var pT=e=>new Date(e).toISOString().slice(0,10),uT=0;async function mT(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,Lj):[]).map(s=>Hj(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await cT(s);await Sn.mkdir(mr(),{recursive:!0});let o=xs.join(mr(),`${pT(t)}.ndjson`);return await Sn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
523
|
+
`).join(""),"utf8"),t-uT>6*3600*1e3&&(uT=t,Wj(t).catch(()=>{})),r.length}async function Wj(e=Date.now()){let t=pT(e-Fj*24*3600*1e3),n=[];try{n=await Sn.readdir(mr())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<t);if(!r.length)return;for(let s of r)await Sn.rm(xs.join(mr(),s),{force:!0});let o=await wT();for(let s of r)delete o[s];await gT(o)}var Fl=new Map;function hT(e,t=Date.now()){let n=e||"unknown",r=Fl.get(n);return r||(Fl.size>512&&Fl.clear(),r={tokens:40,at:t},Fl.set(n,r)),r.tokens=Math.min(40,r.tokens+(t-r.at)/6e4*20),r.at=t,r.tokens<1?!1:(r.tokens-=1,!0)}async function wT(){try{return JSON.parse(await Sn.readFile(fT(),"utf8"))||{}}catch{return{}}}async function gT(e){await Sn.mkdir(mr(),{recursive:!0}),await Sn.writeFile(fT(),JSON.stringify(e),"utf8")}function qj(e,{env:t,host:n}={}){let r=e.name==="app.error"&&e.error,o={};for(let[a,i]of[["event",e.name],["route",e.route],["session",e.sessionId],["anon",e.anonId],["user",e.userId],["release",e.release],["appVersion",e.appVersion],["device",e.deviceClass]])i&&(o[a]=i);let s={timestamp:new Date(e.ts).toISOString(),service:"bostrat-client",source:"treetop-js",env:t||process.env.BOSTRAT_ENV||"pi",host:n||dT.hostname(),level:r?"error":"info",message:r?`${e.error.kind||"Error"}: ${e.error.message||"(no message)"}`.slice(0,8192):e.name,tags:o,attributes:{...e.props?{props:e.props}:{},receivedAt:e.receivedAt}};return r&&(s.error={kind:e.error.kind,message:e.error.message,stack:e.error.stack},e.error.fingerprint&&(s.fingerprint=e.error.fingerprint),e.error.symbolicated&&(s.attributes.symbolicated=!0),e.breadcrumbs&&(s.attributes.breadcrumbs=e.breadcrumbs)),s}async function Kj({fetchImpl:e=fetch}={}){let t=await hv();if(!t)return{sent:0,disabled:!0};let n;try{n=(await Sn.readdir(mr())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await wT(),o=0;for(let s of n){let a=xs.join(mr(),s),i=r[s]||0,c;try{let d=await Sn.open(a,"r");try{let m=(await d.stat()).size;if(m<=i)continue;c=Buffer.alloc(m-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
524
|
+
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=lT){let m=f.slice(d,d+lT).map(w=>qj(w)),h=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:m})}).catch(()=>null);if(!h||!h.ok)return{sent:o,disabled:!1};o+=m.length}r[s]=i+l+1,await gT(r)}return{sent:o,disabled:!1}}var th=null,nh=!1;function yT(){th||(th=setInterval(async()=>{if(!nh){nh=!0;try{await Kj()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{nh=!1}}},Uj),th.unref?.())}import Hl from"node:fs/promises";import ST from"node:path";import Gj from"node:os";var TT=()=>process.env.BOSTRAT_STATE_DIR||ST.join(process.env.HOME||Gj.tmpdir(),".bostrat"),AT=()=>ST.join(TT(),"dictation-glossary.json"),Vj=100,zj=500,Jj=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Wl=null,rh=null,oh=Promise.resolve();function bT(){let e={},t=Date.now();for(let n of Jj)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function ET(){return Wl||(rh??=Hl.readFile(AT(),"utf8").then(e=>JSON.parse(e)?.terms??bT()).catch(()=>bT()).then(e=>Wl=e).finally(()=>rh=null),rh)}async function kT(){let e=JSON.stringify({terms:Wl},null,2),t=AT(),n=`${t}.${process.pid}.tmp`;await Hl.mkdir(TT(),{recursive:!0}),await Hl.writeFile(n,e),await Hl.rename(n,t)}function Yj(){return oh=oh.then(kT,kT),oh.catch(()=>{})}async function sh(){return Object.values(await ET()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var Xj=e=>Math.max(1,Math.ceil(e.length/4));async function Zj(){let e=[],t=0;for(let{term:n}of await sh()){let r=Xj(n);if(!(t+r>zj)&&(e.push(n),t+=r,e.length>=Vj))break}return e}async function Qj(e,t){let n=await ET(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var xT=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Ul=e=>e.toLowerCase();function vT(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function _T(e,t){let n=xT(e),r=xT(t);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]=Ul(n[u])===Ul(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;)Ul(n[c])===Ul(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(vT(r[l])&&i.add(r[l]),l++);for(;l<s;l++)vT(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await Qj(u,"learned");return await Yj(),[...i]}var ii=null;function eL(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return e===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."}:e===401||e===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 ${e}: ${t.slice(0,300)}`}}async function IT(){if(!ii)return null;let e=await Ka();if(!e||e.apiKey.slice(-6)!==ii.keyTail)return null;let{keyTail:t,...n}=ii;return n}async function RT(e,t){let n=await Ka();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 Zj())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":t||"application/octet-stream"},body:e});if(!o.ok){let i=await o.text().catch(()=>""),c=eL(o.status,i);c.persistent&&(ii={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 ii=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}le();var ah={"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 tL(e){if(!e)return null;let t=String(e).trim();if(ah[t])return t;let n=null;for(let r of Object.keys(ah))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function nL(e,t={}){let n=ah[tL(e)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(t[o])||0)*n[o];return r/1e6}function rL(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.error?.code||n?.error?.type||"";return e===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."}:e===401||e===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 (${e}): ${t.slice(0,300)}`}}var ci=null;async function oL(){if(!ci)return null;let e=await yl();if(!e||e.apiKey.slice(-6)!==ci.keyTail)return null;let{keyTail:t,...n}=ci;return n}async function sL(e){let t=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:e.model,audio:{output:{voice:e.voice}}}})});if(!t.ok){let o=await t.text().catch(()=>""),s=rL(t.status,o);s.persistent&&(ci={code:s.code,message:s.message,at:Date.now(),keyTail:e.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}ci=null;let n=await t.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 aL={openai:sL};async function $T(){let e=await yl();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await oL()}:{configured:!1}}async function OT(){let e=await yl();if(!e){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 t=await aL[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function CT(e,{model:t,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=nL(t,s),i=await I(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"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 jT=me.dirname(cL(import.meta.url)),MT=Number(process.env.PORT)||3362,ql=Pm(process.env),LT=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,vs=_S(process.env.BOSTRAT_ALLOW_IPS),dh=IS(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Bl),FT=$S(),UT=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",k=bt();k.disable("x-powered-by");k.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Pl(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});k.use((e,t,n)=>{if(ri(e)||vs.allowed(vs.clientIp(e))||dh.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});k.use(PS);k.use(MS({token:FT,enforce:UT}));k.use("/api/mocks",bt.json({limit:"1mb"}));k.use("/api/mocks",bt.raw({type:"image/png",limit:"24mb"}));k.use("/api/mocks",bt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));k.use("/api/runbooks",bt.json({limit:"1mb"}));k.use("/api/tracks/adopt",bt.json({limit:"1mb"}));k.use("/api/telemetry",bt.json({limit:"256kb"}));k.use("/api/hook",bt.json({limit:"2mb"}));k.use("/api/dictation",bt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));k.use("/api/tracks/:id/upload",bt.raw({type:()=>!0,limit:"25mb"}));k.use(bt.json({limit:"64kb"}));k.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:st,hostTmuxAlive:await Mn(),autoTriggerPct:LT})});k.get("/api/sessions",async(e,t)=>{t.json({sessions:await xt()})});k.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await Nn(n,r||void 0);t.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;t.status(a).json({error:s})}});k.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(n))return t.status(400).json({error:"invalid session name"});if(!await oe(n))return t.status(404).json({error:"session not found"});try{await Ct(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});k.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(n))return t.status(400).json({error:"invalid session name"});let r=await oe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Ls(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});k.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(n))return t.status(400).json({error:"invalid session name"});try{let r=await Yg(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});k.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=tc(n);if(!r)return t.json({running:!1});t.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});var NT=10;k.get("/api/idle-guard",async(e,t)=>{try{t.json(av({tracks:await ce(),views:await Lf(),queuedOf:n=>rs(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});k.get("/api/tracks",async(e,t)=>{let n=(await ce()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>NT){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,NT).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:ja,agentAvailability:await Qc(),tickets:await Rv()})});k.get("/api/cost",async(e,t)=>{try{let n=await ce();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await cS(n);if(r.cursorUsage=Ef(),e.query.format==="text")return t.type("text/plain").send(lS(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/roundup",async(e,t)=>{try{t.json(await fS(await ce(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/spend",async(e,t)=>{try{let n=await gS(await ce(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Ef(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=sa(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/caps",(e,t)=>{t.json(sa())});k.post("/api/usage/caps",async(e,t)=>{try{t.json(await dc(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});k.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await vS(await ce(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await hh(e.get(pi),{byToken:Ip,legacy:()=>ga(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ts("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ts("legacy-denied",{reason:"tokenless-denied",hook:e.body?.hook_event_name}),t.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&e.body?.session_id&&r.box?.sessionId&&e.body.session_id!==r.box.sessionId&&Ts("mismatch",{track:r.track.id,box:r.box.name}),Ts(n,{track:r?.track?.id,hook:e.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await Xv(e.body||{},()=>r);t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});k.post("/api/telemetry",async(e,t)=>{try{if(!hT(vs.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await mT(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});k.get("/api/repos",async(e,t)=>{try{let n=await zt(),r=new Set((await Promise.all(n.map(a=>de(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}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/github/token",async(e,t)=>{try{t.json(await bi(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});k.get("/api/github/status",(e,t)=>{t.json({configured:yi(),calls:tk(),latency:sk()})});k.post("/api/github/refresh",(e,t)=>{Ah(),t.json({ok:!0})});k.get("/api/gmail/search",async(e,t)=>{try{let n=await TS(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});k.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await AS(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function fh(e,t,n){let r=e.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>t&&o?.base===t||n&&o?.repo===n):!r?.base||!t||r.base===t||n&&r.repo===n}async function Kl({repo:e=null,number:t=null,branch:n=null}){let r=e?await de(e):null;if(r&&t!=null){let s=await Je(r,t),a=s&&await ee(s.trackId);if(a)return a}if(!n)return null;let o=await ce();return Oc(o,n,s=>fh(s,e,r))}async function HT({repo:e=null,number:t=null,branch:n=null}){let r=await Kl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=e?await de(e).catch(()=>null):null,s=await ce();return Oc(s,n,a=>!a.archivedAt&&!a.transferredTo&&fh(a,e,o))}async function It(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Kl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await de(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:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function fL(e){let t=(await ce()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of e??[]){if(!n.prs?.length)continue;let r=await de(n.path);if(r)for(let o of n.prs){if(await Je(r,o.number))continue;let s=await Oc(t,o.headRefName,a=>fh(a,n.path,r));s&&await it({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}k.get("/api/git/prs",async(e,t)=>{try{let n=await fk();await fL(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=e.body||{};try{if(s){let w=o||"merge";if(!fa.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let b=await de((await on(n)).path);if(!b)throw new Error("repo has no GitHub remote");let x=await Kl({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 pa(n,y),await it({repo:b,number:y,trackId:x.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ia(b,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Br(b,y,i===!0),await ca(b,y,c===!0&&i!==!0),Bt(),await It("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:x,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}let d=await Uo(n,r,{method:o||"squash"});await It("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let m=await de(n).catch(()=>null),h=!1;if(m&&(i===!0&&await Br(m,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ca(m,d.number,!0).catch(()=>{}),await an(m,d.number).catch(()=>{}),h=await sn(m,d.number).catch(()=>!1),await Gn(m,d.number,"merged").catch(()=>{})),i===!0&&!h){let w=await Kl({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Vr(w.id).catch(()=>{})}t.json(d)}catch(d){let m=d?.message||String(d),h=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(m)?400:502;t.status(h).json({error:m})}});k.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await de((await on(n)).path).catch(()=>null),l=c?await la(c,i).catch(()=>null):null;try{await lf(n,i)}catch(u){if(!l)throw u}Bt(),await It("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});k.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await de(n).catch(()=>null),s=await wk(n,{method:r||"merge",onMerged:a=>{It("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&an(o,a.number).catch(()=>{}).then(()=>sn(o,a.number)).catch(()=>{}).then(()=>Gn(o,a.number,"merged")).catch(()=>{})}});t.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;t.status(a).json({error:s})}});k.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:hk()})});k.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!fa.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await ee(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Dr()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await Br(d.repo,d.number,o===!0).catch(()=>{}),await ca(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let m={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(m);try{let h=await Lr(d.repo);if(!h)throw new Error(`no local checkout for ${d.repo}`);let w=await Fo(h,d.number);f.push({r:m,l:d,repoPath:h,pr:w,plan:pk(w,{update:r!==!1})})}catch(h){m.ok=!1,m.action="failed",m.reason=h?.message||String(h)}}for(let{r:d,l:m,repoPath:h,pr:w,plan:y}of mk(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Uo(h,m.number,{method:i}),await It("merged",{number:m.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c}),await an(m.repo,m.number).catch(()=>{}),await sn(m.repo,m.number).catch(()=>{}),await Gn(m.repo,m.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await pa(h,m.number).catch(()=>{}),d.action="updated-armed"),!await ia(m.repo,m.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await It("auto-armed",{number:m.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(b){d.ok=!1,d.action="failed",d.reason=b?.message||String(b)}if(Bt(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Br(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await ca(d.repo,d.number,!1).catch(()=>{});return t.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});k.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await ee(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Dr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await la(a.repo,a.number).catch(()=>null),c=await Lr(a.repo).catch(()=>null);c&&await lf(c,a.number).catch(()=>{}),i&&(await It("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}))}Bt(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});k.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await ee(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=lt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let y=u.find(b=>b.name===w||b.repo===w||b.base===w||b.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(f=await Go(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let m=[];for(let w of f)try{let y=await df(w.dir,{title:r,body:o,base:s}),b=w.repo||await de(w.dir).catch(()=>null);b&&y.number&&await it({repo:b,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await It("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??w.name}}),m.push({repo:w.name,slug:b??null,ok:!0,url:y.url,number:y.number})}catch(y){m.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let h=m.filter(w=>w.ok);return h.length?t.json({ok:!0,multi:!0,results:m,url:h[0].url,number:h[0].number}):t.status(502).json({error:m.map(w=>`${w.repo}: ${w.error}`).join("; "),results:m})}let c=await df(i.cwd,{title:r,body:o,base:s}),l=await de(i.cwd).catch(()=>null);l&&c.number&&await it({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await It("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});k.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Go(n),o=new Map(r.map(a=>[a.base,a])),s=lt(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}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await jp(e.params.id,n);t.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;t.status(s).json({error:o})}});k.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Lp(e.params.id,e.params.name);t.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;t.status(o).json({error:r})}});k.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Fo(n,r),s=await de(n).catch(()=>null),a=await HT({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=lt(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await gk(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?me.join("ci",i.name):"ci",f=me.join(a.cwd,".bostrat",u),d=me.join(".bostrat",u,`pr-${o.number}-fail.log`);await qe.promises.mkdir(f,{recursive:!0}),await qe.promises.writeFile(me.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
525
|
+
${b.log??"(no fetchable log \u2014 see link)"}
|
|
526
526
|
`).join(`
|
|
527
|
-
`));let p=l.map(k=>k.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await we(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${h}. 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 se({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await jb(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ee("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})}});v.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await jo(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await ut(n).catch(()=>null),a=await IT({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=ie(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 we(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 se({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Lb(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ee("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})}});v.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=cc.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 X(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await B(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 qm(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 we(a.id,Io(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&&_p(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})}});v.post("/api/tracks/:id/pr-exists",async(t,e)=>{let{offerId:n,choice:r}=t.body||{};try{let o=await X(t.params.id);if(!o)return e.status(404).json({error:"track not found"});let{say:s}=await Km(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 ei=t=>e=>({...e,url:SS(e,t.headers)});v.get("/api/views",async(t,e)=>{try{let n=await Ef({trackId:t.query.track||void 0});e.json({views:n.map(ei(t)),maxLive:Gn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.get("/api/views/:id",async(t,e)=>{let n=await jk(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:ei(t)(n),session:Po(n.slug)})});v.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await X(n):null;if(!i&&s&&(i=(await ot()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+dt.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await If({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:ei(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})}});v.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:ei(t)(await Lk(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});v.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:ei(t)(await Fk(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})}});v.delete("/api/views/:id",async(t,e)=>{try{e.json(await Uk(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var Yj=[["linear",qr,sm],["jira",ll,rm]],Qm=new Map;v.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await nm(),r={};for(let[o,s,a]of Yj){let i=await s();if(!i){Qm.delete(o),r[o]={configured:!1};continue}let c=Qm.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},Qm.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)})}});v.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await nm()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await nv(o),i=n?await X(n):null;!i&&r&&(i=(await ot()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+dt.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 Jr=async(t,e)=>{let n=t?await X(t):null;return!n&&e&&(n=(await ot()).find(o=>o.cwd&&(e===o.cwd||e.startsWith(o.cwd+dt.sep)))||null),n};v.get("/api/circuits",async(t,e)=>{try{e.json({circuits:await Zp()})}catch(n){e.status(502).json({error:n?.message||String(n)})}});v.post("/api/circuits/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=await Jr(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 es(o);await sl(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})}});v.post("/api/circuits/unlink",async(t,e)=>{let{trackId:n,cwd:r}=t.body||{};try{let o=await Jr(n,r);if(!o)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await sl(o,null,{source:"chat"}),e.json({ok:!0,trackId:o.id})}catch(o){e.status(502).json({error:o?.message||String(o)})}});var RT=async(t,e,n)=>{if(t)return{ref:t,track:await Jr(e,n)};let r=await Jr(e,n),o=r?await tm(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}};v.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 RT(o,n,r),l=await J0({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})}});v.get("/api/blueprints/show",async(t,e)=>{try{let{ref:n}=await RT(t.query.circuit,t.query.track,t.query.cwd);e.json(await Qp(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})}});v.post("/api/decisions/record",async(t,e)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,alternatives:i,supersedesId:c}=t.body||{};try{let l=await Jr(n,r);if(!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=await X0(l,{title:o,context:s,decision:a,alternatives:i,supersedesId:c});e.json({ok:!0,trackId:l.id,...u})}catch(l){let u=l?.message||String(l);e.status(/required/i.test(u)?400:502).json({error:u})}});v.get("/api/decisions",async(t,e)=>{try{let n=await Jr(t.query.track,t.query.cwd);if(!n)return e.status(400).json({error:"run from inside a Track workspace"});e.json(await Z0(n.id))}catch(n){let r=n?.message||String(n);e.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});v.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 Jr(n,r);if(!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await tm(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 Y0(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})}});v.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await X(n):null;if(!s&&r&&(s=(await ot()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+dt.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 os(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})}});v.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await X(r):null;if(!o&&n&&(o=(await ot()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+dt.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 yv(),ticket:await We(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});v.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await We(t.params.id)})});v.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await X(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await os(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})}});v.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await gm(t.params.id)})});var Fl=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;v.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await vv(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Fl(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await ym(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Fl(r)).json({error:r})}});v.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Sv(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Fl(r)).json({error:r})}});v.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await X(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await Tv(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Fl(s)).json({error:s})}});v.post("/api/tracks/:id/fullscreen-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{e.json(await Fp(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})}});v.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await X(n):null;if(!s&&r&&(s=(await ot()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+dt.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 Ev(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})}});v.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await Av()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});v.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await X(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await _v(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})}});v.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await ga({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.get("/api/mocks/:id",async(t,e)=>{let n=await an(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});var ni={dotfiles:"allow"};v.get("/api/mocks/:id/png",async(t,e)=>{let n=await an(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Te(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=ha(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,ni,s=>{s&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});v.get("/api/mocks/:id/media",async(t,e)=>{let n=await an(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Te(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=ha(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,ni,s=>{s&&!e.headersSent&&e.status(404).json({error:"video artifact missing"})})});v.get("/api/mocks/:id/html",async(t,e)=>{let n=await an(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Te(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(me(n,r.v).html,ni,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});v.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&Bc(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 X(a):null;if(!d&&c&&(d=(await ot()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+dt.sep)))||null),!d)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=(d.boxes||[]).find(h=>h.name===d.leadBox),p=await yx({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})}});v.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n&&Bc(n)?n:null;try{let o=await bx(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})}});v.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await vx(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await we(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})}});v.delete("/api/mocks/:id",async(t,e)=>{try{let n=(await Fx(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Sx(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})}});v.get("/api/flows",async(t,e)=>{try{e.json({flows:await Px({trackId:t.query.track||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});v.get("/api/flows/:id",async(t,e)=>{let n=await Bx(t.params.id);if(!n)return e.status(404).json({error:"flow not found"});e.json({flow:n})});v.post("/api/flows",async(t,e)=>{let{trackId:n,name:r,steps:o,cwd:s}=t.body||{};try{let a=n?await X(n):null;if(!a&&s&&(a=(await ot()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+dt.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 Dx({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})}});v.post("/api/flows/:id/update",async(t,e)=>{try{let{steps:n,note:r}=t.body||{};e.json({ok:!0,flow:await jx(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})}});v.delete("/api/flows/:id",async(t,e)=>{try{e.json(await Lx(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let s=await ot(),a=String(t.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+dt.sep)))?.id,!n)return e.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await zx({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)})}});v.get("/api/runbooks/:id",async(t,e)=>{let n=await Xf(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});v.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Xf(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Gx(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 Jx(n,r.v);return typeof o!="string"?e.status(404).json({error:"runbook artifact missing"}):e.send(o)}e.sendFile(dn(n,r.v),ni,o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});v.get("/api/loops",async(t,e)=>{e.json(await xc())});v.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 ot()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+dt.sep)))||null;if(!a)return e.status(400).json({error:"cwd is not inside a Track workspace"});let i=await xc();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 sf({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})});v.post("/api/loops/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await X(n):null;if(!s&&r&&(s=(await ot()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+dt.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 IS(s,o)})}catch(s){e.status(400).json({error:s?.message||String(s)})}});v.post("/api/tracks/:id/loop-create-offer",async(t,e)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=t.body||{};try{let a=await X(t.params.id);if(!a)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await RS(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);e.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});v.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await X(n):null;if(!a&&s&&(a=(await ot()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+dt.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 Yx({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})}});v.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await Xx(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})}});v.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await Zx(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})}});v.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await Qx(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});v.get("/api/changelog/draft",async(t,e)=>{try{let n=CS(t.query.since||null);e.json({since:n,repos:jm(),prs:await MS(n)})}catch(n){let r=n?.message||String(n);e.status(/unparseable/i.test(r)?400:500).json({error:r})}});v.post("/api/changelog/publish",async(t,e)=>{let{md:n,day:r,trackId:o,cwd:s}=t.body||{};try{let a=o?await X(o):null;if(!a&&s&&(a=(await ot()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+dt.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await NS(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 Xj=120,th=400,Ll=null;v.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>th)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${th} cap; a notification is a nudge, put the detail in chat`});let c=ya(`${n||""}
|
|
528
|
-
${i}`);if(c.length)return
|
|
527
|
+
`));let m=l.map(b=>b.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await yt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${h}. Failing check${l.length===1?"":"s"}: ${m}. The failure log is at ${d} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await it({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Lb(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await It("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(b=>b.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(b=>b.name),log:d})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});k.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Fo(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await de(n).catch(()=>null),a=await HT({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=lt(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 yt(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 it({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Fb(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await It("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.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;t.status(a).json({error:s})}});k.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=No.filter(f=>e.body.actions.includes(f)),!o.length)return t.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 t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await ee(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await B(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let f=await Zm(a,c,{actions:o,notify:s});if(f)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:f.pr})}let l=null;o&&(l=await yt(a.id,Po(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&&Mp(a.id,{actions:o}),t.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;t.status(a).json({error:s})}});k.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await ee(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Qm(o,{offerId:n,choice:r});t.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;t.status(a).json({error:s})}});var li=e=>t=>({...t,url:DS(t,e.headers)});k.get("/api/views",async(e,t)=>{try{let n=await Lf({trackId:e.query.track||void 0});t.json({views:n.map(li(e)),maxLive:Qn})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/views/:id",async(e,t)=>{let n=await fx(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:li(e)(n),session:Vo(n.slug)})});k.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await ee(n):null;if(!i&&s&&(i=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Uf({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});t.json({ok:!0,view:li(e)(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);t.status(l).json({error:c})}});k.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:li(e)(await px(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:li(e)(await mx(e.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;t.status(o).json({error:r})}});k.delete("/api/views/:id",async(e,t)=>{try{t.json(await hx(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var pL=[["linear",Jr,fm],["jira",gl,um]],ih=new Map;k.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await lm(),r={};for(let[o,s,a]of pL){let i=await s();if(!i){ih.delete(o),r[o]={configured:!1};continue}let c=ih.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},ih.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await lm()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await mv(o),i=n?await ee(n):null;!i&&r&&(i=(await ce()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+me.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(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var eo=async(e,t)=>{let n=e?await ee(e):null;return!n&&t&&(n=(await ce()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+me.sep)))||null),n};k.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await sm()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});k.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await eo(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await ls(o);await pl(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});k.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await eo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await pl(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var WT=async(e,t,n)=>{if(e)return{ref:e,track:await eo(t,n)};let r=await eo(t,n),o=r?await im(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}};k.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await WT(o,n,r),l=await iv({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});k.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await WT(e.query.circuit,e.query.track,e.query.cwd);t.json(await am(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});k.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,alternatives:i,supersedesId:c}=e.body||{};try{let l=await eo(n,r);if(!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=await lv(l,{title:o,context:s,decision:a,alternatives:i,supersedesId:c});t.json({ok:!0,trackId:l.id,...u})}catch(l){let u=l?.message||String(l);t.status(/required/i.test(u)?400:502).json({error:u})}});k.get("/api/decisions",async(e,t)=>{try{let n=await eo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await uv(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});k.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await eo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await im(a.id);if(!i)return t.status(400).json({error:"this Track isn't in a circuit \u2014 link one first (bostrat-circuit link \u2026)"});let c=await cv(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});k.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ce()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+me.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await fs(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await ee(r):null;if(!o&&n&&(o=(await ce()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+me.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await $v(),ticket:await Vt(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});k.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Vt(e.params.id)})});k.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await fs(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});k.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Tm(e.params.id)})});var Vl=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;k.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await Nv(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Vl(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Am(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Vl(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await Pv(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Vl(r)).json({error:r})}});k.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await ee(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Bv(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Vl(s)).json({error:s})}});k.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Gp(e.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;t.status(a).json({error:s})}});k.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ce()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+me.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await jv(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await Dv()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});k.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await ee(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Lv(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.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})}});k.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Aa({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/mocks/:id",async(e,t)=>{let n=await dn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var ui={dotfiles:"allow"};k.get("/api/mocks/:id/png",async(e,t)=>{let n=await dn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Et(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Sa(n,r,"png");if(!o)return t.status(404).json({error:"no image for this version (poster extraction failed)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,ui,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});k.get("/api/mocks/:id/media",async(e,t)=>{let n=await dn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Et(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Sa(n,r,"video");if(!o)return t.status(404).json({error:"this version has no video (image or html sourced)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,ui,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});k.get("/api/mocks/:id/html",async(e,t)=>{let n=await dn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Et(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(wt(n,r.v).html,ui,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});k.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&qc(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await ee(a):null;if(!f&&c&&(f=(await ce()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+me.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(h=>h.name===f.leadBox),m=await $x({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});t.json({ok:!0,mock:m})}catch(f){let d=f?.message||String(f);t.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});k.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&qc(n)?n:null;try{let o=await Ox(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.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;t.status(a).json({error:s})}});k.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await Nx(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await yt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.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;t.status(o).json({error:r})}});k.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await Zx(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await Px(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/flows",async(e,t)=>{try{t.json({flows:await Vx({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/flows/:id",async(e,t)=>{let n=await zx(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});k.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await ee(n):null;if(!a&&s&&(a=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.sep)))||null),!a)return t.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 Jx({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.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;t.status(c).json({error:i})}});k.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await Yx(e.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;t.status(o).json({error:r})}});k.delete("/api/flows/:id",async(e,t)=>{try{t.json(await Xx(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ce(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+me.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await a0({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/runbooks/:id",async(e,t)=>{let n=await tp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});k.get("/api/runbooks/:id/md",async(e,t)=>{let n=await tp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=o0(n,e.query.v??null);if(!r)return t.status(404).json({error:"no such version"});if(t.set("content-security-policy","sandbox"),t.set("cache-control","private, max-age=31536000, immutable"),t.type("text/markdown; charset=utf-8"),n.remote){let o=await i0(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(hn(n,r.v),ui,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});k.get("/api/loops",async(e,t)=>{t.json(await Nc())});k.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await ce()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+me.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Nc();if(i.source!=="plane")return t.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 t.status(404).json({error:`Track "${a.name}" is not a loop's run Track \u2014 nothing to report on`});let l=await bf({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});k.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ce()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+me.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await HS(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});k.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await ee(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await WS(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});k.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await ee(n):null;if(!a&&s&&(a=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.sep)))||null),!a)return t.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 c0({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});k.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await l0(e.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;t.status(o).json({error:r})}});k.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await u0(e.params.id,(e.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;t.status(o).json({error:r})}});k.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await d0(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/changelog/draft",async(e,t)=>{try{let n=GS(e.query.since||null);t.json({since:n,repos:Gm(),prs:await VS(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});k.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=o?await ee(o):null;if(!a&&s&&(a=(await ce()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+me.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await zS(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var mL=120,ch=400,Gl=null;k.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>ch)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${ch} cap; a notification is a nudge, put the detail in chat`});let c=Ea(`${n||""}
|
|
528
|
+
${i}`);if(c.length)return t.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 ee(a):null;if(!l&&s&&(l=(await ce()).find(h=>h.cwd&&(s===h.cwd||s.startsWith(h.cwd+me.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(m=>m.name===l.leadBox),f=(m,h)=>String(m).replace(/\s+/g," ").trim().slice(0,h),d={title:n?f(n,mL):l.name,body:f(i,ch),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await I(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),t.json({ok:!0,notification:d,delivered:Gl?Gl.status().connected:null})}catch(i){t.status(500).json({error:i?.message||String(i)})}});k.post("/api/dictation",async(e,t)=>{if(!Buffer.isBuffer(e.body)||e.body.length===0)return t.status(400).json({error:"expected a raw audio body"});try{t.json(await RT(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});k.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await Ka(),terms:await sh(),problem:await IT()})});k.post("/api/dictation/learn",async(e,t)=>{let{transcript:n,finalText:r}=e.body||{};if(typeof n!="string"||typeof r!="string")return t.status(400).json({error:"transcript and finalText are required"});t.json({learned:await _T(n,r)})});k.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await ee(e.params.id))return t.status(404).json({error:"track not found"});t.json(await $T())});k.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});if(n.archivedAt)return t.status(409).json({error:"track is archived"});t.json(await OT())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});k.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=e.body||{};t.json({ok:!0,...await CT(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});k.post("/api/tracks",async(e,t)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c,circuit:l}=e.body||{};try{let u=await ol({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),f=null;if(c)try{await fs(u,c,{source:"create"})}catch(m){f=m?.message||String(m)}let d=null;if(l)try{await pl(u,await ls(l),{source:"picker"})}catch(m){d=m?.message||String(m)}t.json({ok:!0,track:u,...f?{ticketWarning:f}:{},...d?{circuitWarning:d}:{}})}catch(u){let f=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(f)?400:500).json({error:f})}});k.get("/api/tracks/:id",async(e,t)=>{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:qT(cs(e.params.id)),subtasks:zr(e.params.id).map(di)})});k.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Wp(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Tm(e.params.id).catch(()=>{}),t.json(n)}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await Vr(e.params.id)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});k.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await qp(e.params.id)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});k.get("/api/tracks/:id/messages",async(e,t)=>{let n=await el(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});k.get("/api/tracks/:id/events",async(e,t)=>{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),t.flushHeaders();let r=i=>t.write(`id: ${i.seq}
|
|
529
529
|
data: ${JSON.stringify(i)}
|
|
530
530
|
|
|
531
|
-
`),o=Number(
|
|
531
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await B(n))i.seq>o&&r(i);let s=ig(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
532
532
|
|
|
533
|
-
`),25e3);
|
|
534
|
-
`);
|
|
533
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});k.get("/api/tracks/:id/search",async(e,t)=>{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=String(e.query.q||"").trim();if(!r)return t.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(e.query.k)||8,1),50),s=Ys(await B(n)),a=Vi(s,r,{k:o,role:e.query.role||null,tag:e.query.tag||null});t.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}))})});k.get("/api/memory",async(e,t)=>{t.json({stores:await Fh()})});k.delete("/api/memory/:key/:file",async(e,t)=>{let n=String(e.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return t.status(400).json({error:"bad store key"});try{t.json({ok:!0,...await Uh(n,String(e.params.file||""))})}catch(r){let o=r?.message||String(r);t.status(/not found/i.test(o)?404:/bad fact/i.test(o)?400:500).json({error:o})}});var hL=["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(`
|
|
534
|
+
`);k.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await yt(e.params.id,hL)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});k.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await il(e.params.id)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:n?.transferredTo||/mid-turn|terminal|archived/i.test(r)?409:500;t.status(o).json({error:r,...n?.transferredTo?{transferredTo:n.transferredTo}:{}})}});k.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r}=e.body||{};try{t.json(await yt(e.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return t.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});if(o?.transferredTo)return t.status(409).json({error:s,transferredTo:o.transferredTo});t.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});k.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(n.archivedAt)return t.status(409).json({error:"track is archived"});if(!n.cwd)return t.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(e.body)?e.body:null,o=await g0(n.cwd,{filename:e.query.filename,buffer:r});t.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);t.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});k.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(!n.cwd)return t.status(404).json({error:"track has no workspace"});let r=me.basename(String(e.params.file||""));if(!r||!op(me.extname(r).slice(1)))return t.status(400).json({error:"not an upload path"});t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox");let o=rp(n.cwd),s,a;try{s=await qe.promises.realpath(o),a=await qe.promises.realpath(me.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+me.sep))return t.status(404).json({error:"upload not found"});t.sendFile(r,{root:o},i=>{i&&!t.headersSent&&t.status(404).json({error:"upload not found"})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});var wL=/^[0-9a-fA-F-]{8,64}$/;k.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});let{box:r,uuid:o}=e.params,s=Number(e.params.idx);if(!wL.test(o)||!Number.isInteger(s)||s<0)return t.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 t.status(404).json({error:"image source not available"});let i=Zt(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=iL.createInterface({input:qe.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let m;try{m=JSON.parse(d)}catch{continue}if(m.uuid===o){l=m,u.close();break}}let f=l?Nu(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",f.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(f.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await rl(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});k.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await Op(e.params.id);t.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o}=e.body||{};try{t.json(await tl(e.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;t.status(i).json({error:a})}});k.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await Up({provider:e.params.provider,fresh:e.body?.fresh===!0});t.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);t.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});k.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await $p(e.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;t.status(a).json({error:s})}});k.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Bp(e.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;t.status(a).json({error:s})}});k.get("/api/tracks/:id/usage",async(e,t)=>{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=e.query.fresh==="1"||e.query.fresh==="true";t.json(await Cl(r,{fresh:o}))});k.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Dp(e.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;t.status(s).json({error:o})}});k.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await nm(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:di(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return t.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;t.status(c).json({error:i})}});k.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:zr(e.params.id).map(di)})});k.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Rp(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});k.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:di(fl(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});k.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:di(rm(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function di(e){return e?{id:e.id,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,step:e.step,running:!e.finishedAt,box:e.box,branch:e.branch,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}k.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await ll(e.params.id,n,r!==!1,o);t.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 t.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});t.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});k.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await Np(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});k.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(qT(cs(e.params.id))??{running:!1})});k.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await Yp(e.params.id);t.json({ok:!0,step:n.step,startedAt:n.startedAt})}catch(n){let r=n?.message||String(n);t.status(/already running/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});k.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=Jp(e.params.id);if(!n)return t.json({running:!1});t.json({running:!n.finishedAt,step:n.step,error:n.error,startedAt:n.startedAt,finishedAt:n.finishedAt,...n.result??{}})});k.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await ul(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:400).json({error:r})}});k.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await Qp({...e.body||{},trackId:n}))}catch(r){let o=r?.message||String(r);t.status(/already running|already hosted/i.test(o)?409:/not found/i.test(o)?404:400).json({error:o})}});function qT(e){return e?{running:!e.finishedAt,step:e.step,error:e.error,fromBox:e.fromBox,toBox:e.toBox,toAgent:e.toAgent,skipPermissions:e.skipPermissions,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}k.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:$i(),paths:rw,trace:await nw({limit:n})})});var lh=me.resolve(jT,"..","..","client","dist");qe.existsSync(lh)&&(k.use(bt.static(lh)),k.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(me.join(lh,"index.html"),ui)));var KT=lL.createServer(k),gL=new dL({noServer:!0}),GT=(e,t,n)=>{if(!(ri(e)||vs.allowed(vs.clientIp(e))||dh.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
535
535
|
\r
|
|
536
|
-
`),
|
|
536
|
+
`),t.destroy();return}if(BS(e,t,n))return;if(!NS(e,FT,{enforce:UT})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
537
537
|
\r
|
|
538
|
-
`),
|
|
538
|
+
`),t.destroy();return}let r=ww(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
539
539
|
\r
|
|
540
|
-
`),
|
|
540
|
+
`),t.destroy();return}let o=r.sessionName;if(!Ee(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
541
541
|
\r
|
|
542
|
-
`),
|
|
542
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;gL.handleUpgrade(e,t,n,i=>pw(i,o,{cols:s,rows:a}))};KT.on("upgrade",GT);var VT=process.env.BOSTRAT_CERT_DIR||me.join(process.env.HOME||uh.tmpdir(),".bostrat","certs"),PT=Number(process.env.BOSTRAT_TLS_PORT)||3443,BT=me.join(VT,"bostrat.globopi.key"),DT=me.join(VT,"bostrat.globopi.crt");if(qe.existsSync(BT)&&qe.existsSync(DT)){let e=uL.createServer({key:qe.readFileSync(BT),cert:qe.readFileSync(DT)},k);e.on("upgrade",GT),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(PT,ql,()=>{console.log(`[bostrat-agent] https listening on ${ql||"0.0.0.0"}:${PT}`)})}KT.listen(MT,ql,()=>{console.log(`[bostrat-agent] listening on ${ql||"0.0.0.0"}:${MT}`);let e=Zv(process.env);e&&console.warn(`[bostrat-agent] \u26A0 SECURITY: plane-linked node bound to ${e.host?e.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: ${st}`),console.log(`[bostrat-agent] auto-trigger default: ${LT}%`),console.log(`[bostrat-agent] ip allowlist: ${vs.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(dh.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),zp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),em().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Kp().then(t=>t&&console.log(`[bostrat-agent] reaped ${t} orphaned box(es) on archived track(s)`)).catch(t=>console.error("[bostrat-agent] archived-orphan sweep failed:",t?.message||t)),Fp().then(t=>console.log(`[bostrat-agent] standing instructions refreshed (${t.refreshed} updated, ${t.removed} legacy CLAUDE.md removed${t.codexGlobal?`, codex global ${t.codexGlobal}`:""})`)).catch(t=>console.error("[bostrat-agent] standing-instructions refresh failed:",t?.message||t)),xL(),kL(),ab({listTracks:ce,getTrack:ee}).then(t=>console.log(`[bostrat-agent] spend caps ${t.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(t.spent).length} provider counter(s)`)).catch(t=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",t?.message||t)),v0()&&S0({listTracks:ce}),tT(),oT(),Fv({resolveTrack:ee}),Yv({listTracks:ce,createTrack:ol,archiveTrack:Vr,listRepos:zt,resolveTrack:ee,sayToLead:yt,answerPrompt:tl,startLeadHandoff:ll,interruptLead:rl,listSubtaskJobs:zr,cancelSubtask:fl,readEvents:B,appendEvent:I,divertShip:Zm,answerPrExists:Qm,knownAgents:ja}),yL(),Ds().then(t=>{t||console.warn(`[boot] no host tmux server on ${st} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),yT(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Gl=rS({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||uh.hostname(),name:process.env.BOSTRAT_NODE_NAME||uh.hostname(),listTracks:ce,detectAgents:Qc,sampleMetrics:sS,onTracksTransferred:Zp,onUpdateAvailable:t=>_p(t).catch(n=>console.error(`[plane-link] update notice failed: ${n?.message||n}`))}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),ao().catch(()=>{}),m0().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Uv().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),jx().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),t0().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),Gb().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{Gl?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function yL(){bL(),Fc().catch(()=>{}),setInterval(()=>Fc().catch(()=>{}),1e4)}function bL(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=me.resolve(jT,"..","bin",e),n=me.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!qe.existsSync(t))continue;qe.mkdirSync(n,{recursive:!0});let r=me.join(n,e);(qe.existsSync(r)?qe.readlinkSync(r):null)!==t&&(qe.rmSync(r,{force:!0}),qe.symlinkSync(t,r)),console.log(`[bostrat-agent] ${e} CLI linked at ${r}`)}catch(t){console.error(`[bostrat-agent] ${e} CLI link failed:`,t?.message||t)}}function kL(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ce(),r=await bS({tracks:n});r.committed&&console.log(`[state-sync] committed (${r.staged} file(s) staged${r.pushed?", pushed":""})`)}catch{}finally{e=!1}}};setInterval(t,6e4),t()}function xL(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await ce();for(let n of t)n.archivedAt||n.transferredTo||!n.boxes?.some(r=>r.state==="live")||(await el(n.id).catch(()=>{}),await yx(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|